mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-21 21:53:30 +00:00
custom manifest static files storage that also minify scss and js files
This commit is contained in:
parent
2e1f16fa04
commit
b7261ec629
3
.github/workflows/deploy.yml
vendored
3
.github/workflows/deploy.yml
vendored
@ -40,8 +40,7 @@ jobs:
|
|||||||
poetry install --with prod --without docs,tests
|
poetry install --with prod --without docs,tests
|
||||||
poetry run ./manage.py install_xapian
|
poetry run ./manage.py install_xapian
|
||||||
poetry run ./manage.py migrate
|
poetry run ./manage.py migrate
|
||||||
echo "yes" | poetry run ./manage.py collectstatic
|
poetry run ./manage.py collectstatic --clear --noinput
|
||||||
poetry run ./manage.py compilestatic
|
|
||||||
poetry run ./manage.py compilemessages
|
poetry run ./manage.py compilemessages
|
||||||
|
|
||||||
sudo systemctl restart uwsgi
|
sudo systemctl restart uwsgi
|
||||||
|
3
.github/workflows/taiste.yml
vendored
3
.github/workflows/taiste.yml
vendored
@ -39,8 +39,7 @@ jobs:
|
|||||||
poetry install --with prod --without docs,tests
|
poetry install --with prod --without docs,tests
|
||||||
poetry run ./manage.py install_xapian
|
poetry run ./manage.py install_xapian
|
||||||
poetry run ./manage.py migrate
|
poetry run ./manage.py migrate
|
||||||
echo "yes" | poetry run ./manage.py collectstatic
|
poetry run ./manage.py collectstatic --clear --noinput
|
||||||
poetry run ./manage.py compilestatic
|
|
||||||
poetry run ./manage.py compilemessages
|
poetry run ./manage.py compilemessages
|
||||||
|
|
||||||
sudo systemctl restart uwsgi
|
sudo systemctl restart uwsgi
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
#
|
|
||||||
# Copyright 2017
|
|
||||||
# - Sli <antoine@bartuccio.fr>
|
|
||||||
#
|
|
||||||
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
|
|
||||||
# http://ae.utbm.fr.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU General Public License a published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# This program 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 General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License along with
|
|
||||||
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
|
|
||||||
# Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import sass
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
"""Compiles scss in static folder for production."""
|
|
||||||
|
|
||||||
help = "Compile scss files from static folder"
|
|
||||||
|
|
||||||
def compile(self, filename: str):
|
|
||||||
args = {
|
|
||||||
"filename": filename,
|
|
||||||
"include_paths": settings.STATIC_ROOT.name,
|
|
||||||
"output_style": "compressed",
|
|
||||||
}
|
|
||||||
if settings.SASS_PRECISION:
|
|
||||||
args["precision"] = settings.SASS_PRECISION
|
|
||||||
return sass.compile(**args)
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
if not settings.STATIC_ROOT.is_dir():
|
|
||||||
raise Exception(
|
|
||||||
"No static folder availaible, please use collectstatic before compiling scss"
|
|
||||||
)
|
|
||||||
to_exec = list(settings.STATIC_ROOT.rglob("*.scss"))
|
|
||||||
if len(to_exec) == 0:
|
|
||||||
self.stdout.write("Nothing to compile.")
|
|
||||||
sys.exit(0)
|
|
||||||
self.stdout.write("---- Compiling scss files ---")
|
|
||||||
for file in to_exec:
|
|
||||||
# remove existing css files that will be replaced
|
|
||||||
# keeping them while compiling the scss would break
|
|
||||||
# import statements resolution
|
|
||||||
css_file = file.with_suffix(".css")
|
|
||||||
if css_file.exists():
|
|
||||||
css_file.unlink()
|
|
||||||
compiled_files = {file: self.compile(str(file.resolve())) for file in to_exec}
|
|
||||||
for file, scss in compiled_files.items():
|
|
||||||
file.replace(file.with_suffix(".css")).write_text(scss)
|
|
||||||
self.stdout.write(
|
|
||||||
"Files compiled : \n" + "\n- ".join(str(f) for f in compiled_files)
|
|
||||||
)
|
|
@ -18,7 +18,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
<link rel="preload" as="style" href="{{ static('vendored/font-awesome/css/font-awesome.min.css') }}" onload="this.onload=null;this.rel='stylesheet'">
|
<link rel="preload" as="style" href="{{ static('vendored/font-awesome/css/font-awesome.min.css') }}" onload="this.onload=null;this.rel='stylesheet'">
|
||||||
<noscript><link rel="stylesheet" href="{{ static('vendored/font-awesome/css/font-awesome.min.css') }}"></noscript>
|
<noscript><link rel="stylesheet" href="{{ static('vendored/font-awesome/css/font-awesome.min.css') }}"></noscript>
|
||||||
<script defer href="{{ static('vendored/font-awesome/js/fontawesone.min.js') }}"></script>
|
<script defer href="{{ static('vendored/font-awesome/js/fontawesome.min.js') }}"></script>
|
||||||
|
|
||||||
<!-- Jquery declared here to be accessible in every django widgets -->
|
<!-- Jquery declared here to be accessible in every django widgets -->
|
||||||
<script src="{{ static('vendored/jquery/jquery-3.6.2.min.js') }}"></script>
|
<script src="{{ static('vendored/jquery/jquery-3.6.2.min.js') }}"></script>
|
||||||
|
@ -107,4 +107,4 @@ def scss(path):
|
|||||||
if storage.exists(css_path):
|
if storage.exists(css_path):
|
||||||
storage.delete(css_path)
|
storage.delete(css_path)
|
||||||
storage.save(css_path, ContentFile(content))
|
storage.save(css_path, ContentFile(content))
|
||||||
return static(css_path)
|
return static(str(css_path))
|
||||||
|
@ -11,17 +11,31 @@ Nous utilisons du SCSS dans le projet.
|
|||||||
En environnement de développement (`DEBUG=True`),
|
En environnement de développement (`DEBUG=True`),
|
||||||
le SCSS est compilé à chaque fois que le fichier est demandé.
|
le SCSS est compilé à chaque fois que le fichier est demandé.
|
||||||
Pour la production, le projet considère
|
Pour la production, le projet considère
|
||||||
que chacun des fichiers est déjà compilé et,
|
que chacun des fichiers est déjà compilé.
|
||||||
pour ce faire, il est nécessaire
|
C'est pourquoi le SCSS est automatiquement compilé lors
|
||||||
d'utiliser les commandes suivantes dans l'ordre :
|
de la récupération des fichiers statiques.
|
||||||
|
Les fichiers JS sont également automatiquement minifiés.
|
||||||
|
|
||||||
|
Il peut être judicieux de supprimer les anciens fichiers
|
||||||
|
statiques avant de collecter les nouveaux.
|
||||||
|
Pour ça, ajoutez le flag `--clear` à la commande `collectstatic` :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python ./manage.py collectstatic # Pour récupérer tous les fichiers statiques
|
python ./manage.py collectstatic --clear
|
||||||
python ./manage.py compilestatic # Pour compiler les fichiers SCSS qu'ils contiennent
|
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
|
|
||||||
Le dossier où seront enregistrés ces fichiers
|
Le dossier où seront enregistrés ces fichiers
|
||||||
statiques peut être changé en modifiant la variable
|
statiques peut être changé en modifiant la variable
|
||||||
`STATIC_ROOT` dans les paramètres.
|
`STATIC_ROOT` dans les paramètres.
|
||||||
|
|
||||||
|
!!!warning
|
||||||
|
|
||||||
|
La minification des fichiers JS nécessite la présence
|
||||||
|
de `uglifyJS` sur la machine.
|
||||||
|
Pour l'installer, faites la commande suivante (nécessite nodeJS) :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install uglifyjs -g
|
||||||
|
```
|
@ -273,6 +273,15 @@ STATICFILES_FINDERS = [
|
|||||||
"sith.finders.ScssFinder",
|
"sith.finders.ScssFinder",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
STORAGES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||||
|
},
|
||||||
|
"staticfiles": {
|
||||||
|
"BACKEND": "sith.storage.SithStorage",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
# Auth configuration
|
# Auth configuration
|
||||||
AUTH_USER_MODEL = "core.User"
|
AUTH_USER_MODEL = "core.User"
|
||||||
AUTH_ANONYMOUS_MODEL = "core.models.AnonymousUser"
|
AUTH_ANONYMOUS_MODEL = "core.models.AnonymousUser"
|
||||||
@ -716,7 +725,7 @@ if TESTING:
|
|||||||
"BACKEND": "django.core.files.storage.InMemoryStorage",
|
"BACKEND": "django.core.files.storage.InMemoryStorage",
|
||||||
},
|
},
|
||||||
"staticfiles": {
|
"staticfiles": {
|
||||||
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
"BACKEND": "sith.storage.SithStorage",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
73
sith/storage.py
Normal file
73
sith/storage.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
import sass
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib.staticfiles.storage import (
|
||||||
|
ManifestStaticFilesStorage,
|
||||||
|
)
|
||||||
|
from django.core.files.storage import Storage
|
||||||
|
|
||||||
|
|
||||||
|
class SithStorage(ManifestStaticFilesStorage):
|
||||||
|
def _compile_scss(self):
|
||||||
|
to_exec = list(settings.STATIC_ROOT.rglob("*.scss"))
|
||||||
|
if len(to_exec) == 0:
|
||||||
|
return
|
||||||
|
for file in to_exec:
|
||||||
|
# remove existing css files that will be replaced
|
||||||
|
# keeping them while compiling the scss would break
|
||||||
|
# import statements resolution
|
||||||
|
css_file = file.with_suffix(".css")
|
||||||
|
if css_file.exists():
|
||||||
|
css_file.unlink()
|
||||||
|
scss_paths = [p.resolve() for p in to_exec if p.suffix == ".scss"]
|
||||||
|
base_args = {"output_style": "compressed", "precision": settings.SASS_PRECISION}
|
||||||
|
compiled_files = {
|
||||||
|
p: sass.compile(filename=str(p), **base_args) for p in scss_paths
|
||||||
|
}
|
||||||
|
for file, scss in compiled_files.items():
|
||||||
|
file.replace(file.with_suffix(".css")).write_text(scss)
|
||||||
|
|
||||||
|
# once the files are compiled, the manifest must be updated
|
||||||
|
# to have the right suffix
|
||||||
|
new_entries = {
|
||||||
|
k.replace(".scss", ".css"): self.hashed_files.pop(k).replace(
|
||||||
|
".scss", ".css"
|
||||||
|
)
|
||||||
|
for k in list(self.hashed_files.keys())
|
||||||
|
if k.endswith(".scss")
|
||||||
|
}
|
||||||
|
self.hashed_files.update(new_entries)
|
||||||
|
self.save_manifest()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _minify_js():
|
||||||
|
try:
|
||||||
|
subprocess.run(["uglifyjs", "-v"])
|
||||||
|
except FileNotFoundError:
|
||||||
|
warnings.warn(
|
||||||
|
"Couldn't minify JS files. Make sure UglifyJs is installed.",
|
||||||
|
stacklevel=1,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
to_exec = [
|
||||||
|
p for p in settings.STATIC_ROOT.rglob("*.js") if ".min" not in p.suffixes
|
||||||
|
]
|
||||||
|
for path in to_exec:
|
||||||
|
p = path.resolve()
|
||||||
|
subprocess.run(["uglifyjs", p, "-o", p])
|
||||||
|
logging.getLogger("main").info(f"Minified {path}")
|
||||||
|
|
||||||
|
def post_process(
|
||||||
|
self, paths: dict[str, tuple[Storage, str]], *, dry_run: bool = False
|
||||||
|
):
|
||||||
|
# Whether we get the files that were processed by ManifestFilesMixin
|
||||||
|
# by calling super() or whether we get them from the manifest file
|
||||||
|
# makes no difference - we have to open the manifest file anyway
|
||||||
|
# because we need to update the paths stored inside it.
|
||||||
|
yield from super().post_process(paths, dry_run)
|
||||||
|
|
||||||
|
self._compile_scss()
|
||||||
|
self._minify_js()
|
Loading…
Reference in New Issue
Block a user