diff --git a/.github/setup_evap/action.yml b/.github/setup_evap/action.yml index 1a2f088a07..f5b29fb758 100644 --- a/.github/setup_evap/action.yml +++ b/.github/setup_evap/action.yml @@ -14,6 +14,9 @@ inputs: description: "whether or not to run `npm ci`" required: false default: false + working-directory: + required: false + default: "." runs: using: "composite" @@ -23,17 +26,21 @@ runs: - uses: nicknovitski/nix-develop@v1 with: arguments: "${{ inputs.shell }}" + if: ${{ inputs.shell != '' }} - name: Add localsettings run: cp evap/settings_test.py evap/localsettings.py shell: bash + working-directory: ${{ inputs.working-directory }} - name: Install Node dependencies run: npm ci shell: bash if: ${{ inputs.npm-ci }} + working-directory: ${{ inputs.working-directory }} - name: Start database run: nix run .#services -- --detached && nix run .#wait-for-pc shell: bash if: ${{ inputs.start-db }} + working-directory: ${{ inputs.working-directory }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..2f3343c0a5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: EvaP Release + +on: + pull_request: + # release: + # types: [published] + +jobs: + pypi-publish: + name: upload release to PyPI + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix run .#build-dist + - run: tar tvf dist/*.tar.gz + - run: unzip -l dist/*.whl + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 66007d4ea4..c30442f638 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,7 @@ on: branches: - main pull_request: + workflow_dispatch: jobs: test: @@ -98,30 +99,53 @@ jobs: - uses: actions/checkout@v4 with: submodules: true - - uses: ./.github/setup_evap + path: main + - uses: ./main/.github/setup_evap with: - shell: .#evap # no dev-dependencies + shell: "" start-db: true + working-directory: main - - name: Install additional dependencies - run: sudo apt-get update && sudo apt-get install gettext + - name: Build wheel + run: nix run .#build-dist + working-directory: main - - name: GitHub actions has wrong file ownership here, the checkout actions has a problem here (see their 1049) - run: | - git config --global --add safe.directory '*' - sudo -H -u root git config --global --add safe.directory '*' + - uses: actions/checkout@v4 + with: + repository: e-valuation/evap-deployment + ref: work # todo: remove + path: deployment + + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install wheel + run: pip install main/dist/*.whl - name: Load test data run: | - python manage.py migrate - python manage.py loaddata test_data + ln -s ../main/data/ + cat <(echo 'from evap.settings import *') ../main/evap/settings_test.py | tee deployment_settings.py + python -m evap migrate + python -m evap loaddata test_data + working-directory: deployment + env: + DJANGO_SETTINGS_MODULE: deployment_settings - name: Backup database - run: deployment/update_production.sh backup.json + run: ./update_production.sh backup.json env: EVAP_OVERRIDE_BACKUP_FILENAME: true - EVAP_SKIP_CHECKOUT: true + EVAP_SKIP_UPDATE: true + EVAP_SKIP_APACHE_STEPS: true + DJANGO_SETTINGS_MODULE: deployment_settings + working-directory: deployment - name: Reload backup - run: echo "yy" | deployment/load_production_backup.sh backup.json + run: echo "yy" | ./load_production_backup.sh backup.json + env: + EVAP_SKIP_APACHE_STEPS: true + DJANGO_SETTINGS_MODULE: deployment_settings + working-directory: deployment compile_scss: runs-on: ubuntu-22.04 diff --git a/.gitignore b/.gitignore index caa4789b98..71038aab13 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ TAGS # python files *.py[cod] +dist/ # gettext binaries *.mo diff --git a/deployment/apache.maintenance-template.conf b/deployment/apache.maintenance-template.conf deleted file mode 100644 index a246186f78..0000000000 --- a/deployment/apache.maintenance-template.conf +++ /dev/null @@ -1,24 +0,0 @@ - - ServerName evap - - DocumentRoot /var/www/html - - RewriteEngine on - RewriteCond %{DOCUMENT_ROOT}/maintenance.html -f - RewriteCond %{SCRIPT_FILENAME} !maintenance.html - RewriteRule !\.(png|css|svg)$ /maintenance.html [R=503,L] - RewriteRule maintenance\.css$ /maintenance.css - RewriteRule triangles_gray\.svg$ /background_gray.svg - RewriteRule triangles_color\.svg$ /background_color.svg - RewriteRule favicon\.png$ /favicon.png - ErrorDocument 503 /maintenance.html - Header Set Cache-Control "max-age=0, no-store" - - ErrorLog /var/log/apache2/error.log - - # Possible values include: debug, info, notice, warn, error, crit, - # alert, emerg. - LogLevel info - - CustomLog /var/log/apache2/access.log combined - diff --git a/deployment/apache.template.conf b/deployment/apache.template.conf deleted file mode 100644 index e2aceec320..0000000000 --- a/deployment/apache.template.conf +++ /dev/null @@ -1,33 +0,0 @@ - - ServerName evap - - DocumentRoot /var/www/ - - WSGIScriptAlias / ${REPO_FOLDER}/evap/wsgi.py - WSGIDaemonProcess evap processes=2 threads=15 display-name=%{GROUP} user=evap python-home=${ENV_FOLDER} - WSGIProcessGroup evap - - Alias /static ${REPO_FOLDER}/evap/static_collected - - # Cache static assets for at least three hours - Header set Cache-Control "max-age=10800" - - - # Cache static assets with appended hash for one year - Header set Cache-Control "max-age=31536000, immutable" - - - - - Order deny,allow - Require all granted - - - ErrorLog /var/log/apache2/error.log - - # Possible values include: debug, info, notice, warn, error, crit, - # alert, emerg. - LogLevel info - - CustomLog /var/log/apache2/access.log combined - diff --git a/deployment/disable_maintenance_mode.sh b/deployment/disable_maintenance_mode.sh deleted file mode 100755 index 70d8d44d00..0000000000 --- a/deployment/disable_maintenance_mode.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e # abort on error - -rm -f /var/www/html/maintenance.css -rm -f /var/www/html/background_gray.svg -rm -f /var/www/html/background_color.svg -rm -f /var/www/html/favicon.png -rm -f /var/www/html/maintenance.html -a2ensite -q evap.conf -a2dissite -q evap-maintenance.conf -service apache2 restart -echo "Maintenance mode disabled." diff --git a/deployment/enable_maintenance_mode.sh b/deployment/enable_maintenance_mode.sh deleted file mode 100755 index a8f97f1dea..0000000000 --- a/deployment/enable_maintenance_mode.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -set -e # abort on error - -cp /opt/evap/evap/static/css/evap.css /var/www/html/maintenance.css -cp /opt/evap/evap/static/images/triangles_gray.svg /var/www/html/background_gray.svg -cp /opt/evap/evap/static/images/triangles_color.svg /var/www/html/background_color.svg -cp /opt/evap/evap/static/images/favicon_64.png /var/www/html/favicon.png -cp /opt/evap/evap/static/maintenance/maintenance.html /var/www/html/maintenance.html -a2ensite -q evap-maintenance.conf -a2dissite -q evap.conf -service apache2 restart -echo "Maintenance mode enabled." diff --git a/deployment/load_production_backup.sh b/deployment/load_production_backup.sh deleted file mode 100755 index 42744ac496..0000000000 --- a/deployment/load_production_backup.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash - -# Counter part for update_production script. -# This script will import the backup made by update_production. - -set -e # abort on error -cd "$(dirname "$0")/.." # change to root directory - -CONDITIONAL_NOINPUT="" -[[ ! -z "$GITHUB_WORKFLOW" ]] && echo "Detected GitHub" && CONDITIONAL_NOINPUT="--noinput" && EVAP_SKIP_APACHE_STEPS=1 - -COMMIT_HASH="$(git rev-parse --short HEAD)" - -# argument 1 is the filename for the backupfile. -if [ ! $# -eq 1 ] # if there is exactly one argument - then - echo "Please specify a backup file to import as command line argument." - exit -fi - -# Check if commit hash is in file name. Ask for confirmation if its not there. -if [[ ! $1 =~ ${COMMIT_HASH} ]] -then - echo "Looks like the backup was made on another commit. Currently, you are on ${COMMIT_HASH}." - read -p "Do you want to continue [y]? " -n 1 -r - echo - - if [[ ! $REPLY =~ ^[Yy]$ ]] - then - exit 1 - fi -fi - -echo "WARNING! This will cause IRREPARABLE DATA LOSS." -read -p "Are you sure you want to continue [y]? " -n 1 -r -echo -if [[ ! $REPLY =~ ^[Yy]$ ]] -then - exit 1 -fi - -[[ -z "$EVAP_SKIP_APACHE_STEPS" ]] && sudo service apache2 stop - -# sometimes, this fails for some random i18n test translation files. -./manage.py compilemessages || true -./manage.py scss --production -./manage.py collectstatic --noinput - -./manage.py reset_db "$CONDITIONAL_NOINPUT" -./manage.py migrate -./manage.py flush "$CONDITIONAL_NOINPUT" -./manage.py loaddata_unlogged "$1" - -./manage.py clear_cache --all -v=1 -./manage.py refresh_results_cache - -[[ -z "$EVAP_SKIP_APACHE_STEPS" ]] && sudo service apache2 start - -{ set +x; } 2>/dev/null # don't print the echo command, and don't print the 'set +x' itself - -echo "Backup restored." diff --git a/deployment/update_production.sh b/deployment/update_production.sh deleted file mode 100755 index eee24df57a..0000000000 --- a/deployment/update_production.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -set -e # abort on error -cd "$(dirname "$0")/.." # change to root directory - -echo "$PWD" - -# used for constructing the backup file name -COMMIT_HASH="$(git rev-parse --short HEAD)" -BACKUP_TITLE="backup" -TIMESTAMP="$(date +%Y-%m-%d_%H:%M:%S)" - -[[ ! -z "$GITHUB_WORKFLOW" ]] && echo "Detected GitHub" && EVAP_SKIP_APACHE_STEPS=1 - -# argument 1 is the title for the backupfile. -if [ $# -eq 1 ] - then - BACKUP_TITLE=$1 -fi - -FILENAME="${BACKUP_TITLE}_${TIMESTAMP}_${COMMIT_HASH}.json" - -[[ -z "$EVAP_OVERRIDE_BACKUP_FILENAME" ]] || echo "Overriding Automatic Filename" -[[ -z "$EVAP_OVERRIDE_BACKUP_FILENAME" ]] || FILENAME="${BACKUP_TITLE}" - -echo "Backup will be stored in $FILENAME" -echo "Starting update..." - -set -x # print executed commands. enable this here to not print the if above. - -git fetch - -# Note that apache should not be running during most of the upgrade, -# since then e.g. the backup might be incomplete or the code does not -# match the database layout, or https://github.com/e-valuation/EvaP/issues/1237. -[[ -z "$EVAP_SKIP_APACHE_STEPS" ]] && sudo ./deployment/enable_maintenance_mode.sh - -./manage.py dumpdata --natural-foreign --natural-primary --all -e contenttypes -e auth.Permission --indent 2 --output "$FILENAME" - -[[ ! -z "$EVAP_SKIP_CHECKOUT" ]] && echo "Skipping Checkout" -[[ ! -z "$EVAP_SKIP_CHECKOUT" ]] || git checkout origin/release -# NOTE: The Python environment should be updated here with something like -# sudo -H -u "$USERNAME" "$ENVDIR/bin/pip" install -r requirements.txt - -# sometimes, this fails for some random i18n test translation files. -./manage.py compilemessages || true -./manage.py scss --production -./manage.py ts compile --fresh -./manage.py collectstatic --noinput -./manage.py migrate -./manage.py clear_cache --all -v=1 -./manage.py refresh_results_cache - -[[ -z "$EVAP_SKIP_APACHE_STEPS" ]] && sudo ./deployment/disable_maintenance_mode.sh - -{ set +x; } 2>/dev/null # don't print the echo command, and don't print the 'set +x' itself - -echo "Update completed." diff --git a/evap/__main__.py b/evap/__main__.py new file mode 100755 index 0000000000..f33754f5ec --- /dev/null +++ b/evap/__main__.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import os +import sys + +from django.conf import settings +from django.core.management import execute_from_command_line + + +def main(): + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "evap.settings") + if getattr(settings, "DATADIR", None) is not None: + settings.DATADIR.mkdir(exist_ok=True) + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/deployment/localsettings.template.py b/evap/development/localsettings.template.py similarity index 87% rename from deployment/localsettings.template.py rename to evap/development/localsettings.template.py index 71e27dd760..0258d5c4f2 100644 --- a/deployment/localsettings.template.py +++ b/evap/development/localsettings.template.py @@ -1,17 +1,19 @@ +# noqa: N999 + from fractions import Fraction from pathlib import Path from django.utils.safestring import mark_safe DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': 'evap', - 'USER': 'evap', - 'PASSWORD': 'evap', + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": "evap", + "USER": "evap", + "PASSWORD": "evap", # Absolute path to use unix domain socket - 'HOST': Path("./data/").resolve(), - 'CONN_MAX_AGE': 600, + "HOST": Path("./data/").resolve(), + "CONN_MAX_AGE": 600, } } diff --git a/deployment/manage_autocompletion.sh b/evap/development/manage_autocompletion.sh similarity index 100% rename from deployment/manage_autocompletion.sh rename to evap/development/manage_autocompletion.sh diff --git a/evap/static/maintenance/maintenance.html b/evap/static/maintenance/maintenance.html deleted file mode 100644 index 2a8d80361a..0000000000 --- a/evap/static/maintenance/maintenance.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - EvaP - - - - -
-
-
-
-

Wartung

-
-
- EvaP wird gerade auf den neuesten Stand gebracht. Bitte versuchen Sie es in ein paar Minuten noch einmal. -
-
-
- - - -
-
-

Maintenance

-
-
- EvaP is being updated right now. Please try again in a few minutes. -
-
-
-
-
- - diff --git a/evap/wsgi.py b/evap/wsgi.py index 5d4e2ff0a2..60e0fbf7fa 100644 --- a/evap/wsgi.py +++ b/evap/wsgi.py @@ -7,15 +7,6 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ """ -import os -import sys - from django.core.wsgi import get_wsgi_application -# this adds the project root to the python path. -PWD = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -sys.path = [PWD] + sys.path - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "evap.settings") - application = get_wsgi_application() diff --git a/flake.nix b/flake.nix index bf6c900bdf..5cf247f535 100644 --- a/flake.nix +++ b/flake.nix @@ -90,6 +90,24 @@ done ''; }; + + build-dist = pkgs.writeShellApplication { + name = "build-dist"; + runtimeInputs = with pkgs; [ nodejs gettext git ]; + text = + let + python-dev = self.devShells.${system}.evap-dev.passthru.venv; + python-build = self.packages.${system}.python3.withPackages (ps: [ ps.build ]); + in + '' + set -x + npm ci + ${python-dev}/bin/python ./manage.py compilemessages + ${python-dev}/bin/python ./manage.py scss --production + ${python-dev}/bin/python ./manage.py ts compile --fresh + ${python-build}/bin/python -m build + ''; + }; }); }; } diff --git a/manage.py b/manage.py index 3dac837a2d..703b7a085f 100755 --- a/manage.py +++ b/manage.py @@ -1,13 +1,5 @@ #!/usr/bin/env python3 -import os -import sys +from evap.__main__ import main -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "evap.settings") - - from django.conf import settings - from django.core.management import execute_from_command_line - - settings.DATADIR.mkdir(exist_ok=True) - execute_from_command_line(sys.argv) +main() diff --git a/nix/services.nix b/nix/services.nix index f2202df332..76bd684631 100644 --- a/nix/services.nix +++ b/nix/services.nix @@ -63,7 +63,7 @@ exit 0 fi set -x - cp deployment/localsettings.template.py evap/localsettings.py + cp evap/development/localsettings.template.py evap/localsettings.py sed -i -e "s/\$SECRET_KEY/$(head /dev/urandom | LC_ALL=C tr -dc A-Za-z0-9 | head -c 32)/" evap/localsettings.py git submodule update --init ./manage.py collectstatic --noinput diff --git a/nix/shell.nix b/nix/shell.nix index 59f939b44c..94086e2135 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -4,7 +4,7 @@ let # When running a nix shell, XDG_DATA_DIRS will be populated so that bash_completion can (lazily) find this completion script evap-managepy-completion = pkgs.runCommand "evap-managepy-completion" { } '' mkdir -p "$out/share/bash-completion/completions" - install ${../deployment/manage_autocompletion.sh} "$out/share/bash-completion/completions/manage.py.bash" + install ${../evap/development/manage_autocompletion.sh} "$out/share/bash-completion/completions/manage.py.bash" ''; clean-setup = pkgs.writeShellScriptBin "clean-setup" '' diff --git a/pyproject.toml b/pyproject.toml index 37375a14ad..191010adca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "evap" description = "EvaP" -version = "0.0.0" +version = "2024.11.12" readme = "README.md" requires-python = "~=3.10.0" dependencies = [ @@ -39,13 +39,31 @@ dev = [ "typeguard~=4.4.0", ] +[tool.uv] +no-binary-package = [ + "psycopg-c", +] + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" -[tool.uv] -no-binary-package = [ - "psycopg-c", +[tool.hatch.build] +skip-excluded-dirs = false # otherwise, the artifacts below will be skipped +exclude = [ + "evap/static/bootstrap", + "evap/static/font-awesome", +] +artifacts = [ + "evap/static/css/evap.css", + "evap/static/css/evap.css.map", + "evap/static/js/*.js", + "evap/static/js/*.map", + "evap/static/bootstrap/dist/js/bootstrap.bundle.min.js", + "evap/static/bootstrap/dist/js/bootstrap.bundle.min.js.map", + "evap/static/font-awesome/webfonts/", + + "evap/locale/*.mo", ] ############################################## diff --git a/uv.lock b/uv.lock index 01f675ccd7..e025799233 100644 --- a/uv.lock +++ b/uv.lock @@ -312,7 +312,7 @@ wheels = [ [[package]] name = "evap" -version = "0.0.0" +version = "2024.11.7" source = { editable = "." } dependencies = [ { name = "django" },