From 1a9c7a650f62c0eb140c0a0abded12109682696e Mon Sep 17 00:00:00 2001 From: Miles Wells Date: Thu, 23 Jul 2026 17:22:43 +0100 Subject: [PATCH 1/9] Add DOI badge https://github.com/cortex-lab/alyx/issues/1008 --- CHANGELOG.md | 2 +- README.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index affe6b02..6b937c91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Worklfow tests now run in parallel and asserts no new migrations +- Workflow tests now run in parallel and asserts no new migrations ## [3.6.0] diff --git a/README.md b/README.md index 84d9aacb..d9fcd95b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Github Actions](https://github.com/cortex-lab/alyx/actions/workflows/main.yml/badge.svg)](https://github.com/cortex-lab/alyx/actions/) [![Coverage Status](https://coveralls.io/repos/github/cortex-lab/alyx/badge.svg?branch=github_action)](https://coveralls.io/github/cortex-lab/alyx?branch=master) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21513696.svg)](https://doi.org/10.5281/zenodo.21513696) Database for experimental neuroscience laboratories From f6ff05718adcdef28adc5b814db475ae9d584e49 Mon Sep 17 00:00:00 2001 From: Miles Wells Date: Thu, 23 Jul 2026 17:23:06 +0100 Subject: [PATCH 2/9] Use commit message on squash commit release --- .github/workflows/release.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f3ad0c23..ef31053e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,6 +52,7 @@ jobs: - uses: actions/checkout@v4 with: ref: ${{ github.event.workflow_run.head_sha }} + fetch-depth: 0 - name: Create tag and GitHub release run: | @@ -59,7 +60,16 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git tag -a "$VERSION" -m "Release $VERSION" git push origin "$VERSION" - gh release create "$VERSION" --title "$VERSION" --generate-notes + + PREV_TAG=$(git describe --tags --abbrev=0 --match '[0-9]*.[0-9]*.[0-9]*' "$VERSION^" 2>/dev/null || true) + if [[ -n "$PREV_TAG" && "$(git rev-list --count "$PREV_TAG..$VERSION")" == "1" ]]; then + # Single squash commit since the last release: use its message verbatim + # instead of --generate-notes, which would just restate the same one commit. + NOTES="$(git log -1 --format=%B "$VERSION")" + gh release create "$VERSION" --title "$VERSION" --notes "$NOTES" + else + gh release create "$VERSION" --title "$VERSION" --generate-notes + fi # A tag pushed by GITHUB_TOKEN does not trigger build-image.yml's push-tags event # (GitHub suppresses it to avoid recursion), so dispatch the build explicitly. From b1ebb4662f5d0be2f33dad59b83e207fb8f8a8d8 Mon Sep 17 00:00:00 2001 From: Miles Wells Date: Thu, 23 Jul 2026 17:39:29 +0100 Subject: [PATCH 3/9] Omit WSGI files from coverage --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index 84140abf..d98a681c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,8 @@ source = alyx concurrency = multiprocessing parallel = True omit = + */wsgi.py + */gunicorn_wsgi.py [coverage:report] exclude_lines = From 7273ccb96b908a5c5f3e0e36d0068027f6c270bb Mon Sep 17 00:00:00 2001 From: Miles Wells Date: Thu, 23 Jul 2026 18:14:44 +0100 Subject: [PATCH 4/9] Update readme with release process --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d9fcd95b..830d67df 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,18 @@ Contribution checklist: - [ ] update version number in `./alyx/alyx/__init__.py` - [ ] update `CHANGELOG.md` +Release process: +1. Open a PR from your feature branch into dev +2. On dev, bump the Alyx version and update the changelog +3. Open a PR from dev to master +4. When CI passes, make a squash commit into master using the version as the commit title, and changelog section as the message +5. Actions will automatically create a new release, deploy containers, and assign a DOI ### Running tests Continuous integration is set up. But before submitting a PR or commit,the tests can run locally. First install the test dependencies with `pip install -r requirements_test.txt`. - `./manage.py test -n --parallel` parallel test without migrations (fastest) - - `./manage.py test` test with migrations (recommended if model changes) + - `./manage.py test` test with migrations (recommended if models change) ### Documentation contribution guide From 22bef7451a8ddda81ca0b1d8928d121b6a3ba942 Mon Sep 17 00:00:00 2001 From: Olivier Winter Date: Fri, 24 Jul 2026 12:09:39 +0200 Subject: [PATCH 5/9] Set COMPOSE_PROJECT_NAME=alyx-deploy as the fixed project name for ~/app deploys --- deploy/app/template.env | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy/app/template.env b/deploy/app/template.env index 4176616e..bf8a9463 100644 --- a/deploy/app/template.env +++ b/deploy/app/template.env @@ -13,6 +13,7 @@ GLOBUS_CLIENT_ID=525cc543-8ccb-4d11-8036-af332da5eafd TZ=UTC # Alyx datetime timezone. See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones DJANGO_MEDIA_ROOT=~/uploaded DJANGO_TABLES_ROOT=~/tables +COMPOSE_PROJECT_NAME=alyx-deploy # DJANGO_MEDIA_ROOT=~/scratch/uploaded # add if local storage # DJANGO_TABLES_ROOT=~/scratch/uploaded # add if local storage # DJANGO_MEDIA_S3=https://alyx-uploaded.s3.eu-west-2.amazonaws.com/uploaded/ # add if s3 (overrides local media) From caa59186e9648b5d306f5fdbaff1bdaceb19acae Mon Sep 17 00:00:00 2001 From: Olivier Winter Date: Fri, 24 Jul 2026 12:31:37 +0200 Subject: [PATCH 6/9] Pin ruff==0.15.22 to stop CI silently breaking on new ruff releases ruff is pulled in transitively via ONE-api, unpinned -- a new ruff 0.16.0 release added default rules that surfaced 821 new findings overnight with zero code changes on this branch. --- requirements_test.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/requirements_test.txt b/requirements_test.txt index 17ffb944..f1154b5f 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,10 @@ # for code coverage coverage coveralls +# pin ruff explicitly -- it's otherwise pulled in transitively and unpinned via ONE-api, +# so a new ruff release can silently start failing CI with no code change at all (see +# cortex-lab/alyx#1027: ruff 0.16.0 added new default rules, 821 new findings overnight) +ruff==0.15.22 # for ONE cache table generation tests pyarrow pandas From c8949b5014a93af60ce539897d9e1f2ed0d9c646 Mon Sep 17 00:00:00 2001 From: Miles Wells Date: Fri, 24 Jul 2026 12:15:42 +0100 Subject: [PATCH 7/9] Scoped delete_zygosity_rule post delete hook By default this post delete hook was registered to all db models, including Datasets. This made bulk deletes slow and memory intensive as all objects were instantiated by Django before delete. Now this hook is only registered to ZygosityRule, as intended. --- alyx/subjects/models.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/alyx/subjects/models.py b/alyx/subjects/models.py index d3627715..a6da282b 100644 --- a/alyx/subjects/models.py +++ b/alyx/subjects/models.py @@ -892,10 +892,9 @@ def genotype_from_litter(self, subject, force=False): self._create_zygosity(subject, allele, z, force=force) -@receiver(post_delete) +@receiver(post_delete, sender=ZygosityRule) def delete_zygosity_rule(sender, instance, **kwargs): - if isinstance(instance, ZygosityRule): - _update_zygosities(instance.line, instance.sequence0) + _update_zygosities(instance.line, instance.sequence0) class AlleleManager(models.Manager): From 4c3e3a537297456c723af02f417fa806dda08feb Mon Sep 17 00:00:00 2001 From: Miles Wells Date: Fri, 24 Jul 2026 12:12:19 +0100 Subject: [PATCH 8/9] Remove dj_database_url When using passwords that contain special chars (e.g. AWS managed secrets) the password chars must be escaped. This makes using the URL format no longer useful --- deploy/app/docker/settings-deploy.py | 22 +++++++++++----------- requirements.txt | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/deploy/app/docker/settings-deploy.py b/deploy/app/docker/settings-deploy.py index 7a6b415b..de8ce9e2 100644 --- a/deploy/app/docker/settings-deploy.py +++ b/deploy/app/docker/settings-deploy.py @@ -10,7 +10,6 @@ import os import json -import dj_database_url import logging import dotenv import urllib.parse @@ -31,16 +30,17 @@ # %% Databases SECRET_KEY = os.getenv('DJANGO_SECRET_KEY') -# Build the connection URL -POSTGRES_USER = urllib.parse.quote(os.getenv('POSTGRES_USER', '')) -POSTGRES_PASSWORD = urllib.parse.quote(os.getenv('POSTGRES_PASSWORD', '')) -POSTGRES_HOST = urllib.parse.quote(os.getenv('POSTGRES_HOST', '')) -POSTGRES_PORT = urllib.parse.quote(os.getenv('POSTGRES_PORT', '5432')) # Default PostgreSQL port -POSTGRES_DB = urllib.parse.quote(os.getenv('POSTGRES_DB', '')) -# the database details are provided in the form of an URL. The URL looks like: -# "postgres://USER:PASSWORD@HOST:PORT/DB_NAME" -database_url = f"postgres://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}" -DATABASES = {"default": dj_database_url.parse(database_url)} +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "USER": urllib.parse.quote(os.getenv('POSTGRES_USER', '')), + "PASSWORD": urllib.parse.quote(os.getenv('POSTGRES_PASSWORD', '')), + "HOST": urllib.parse.quote(os.getenv('POSTGRES_HOST', '')), + "NAME": urllib.parse.quote(os.getenv('POSTGRES_DB', '')), + # Default PostgreSQL port + "PORT": urllib.parse.quote(os.getenv('POSTGRES_PORT', '5432')) + } +} # %% S3 access to write cache tables # the s3 access details are provided in the form of a JSON string. The variable looks like: # S3_ACCESS={"access_key":"xxxxx", "secret_key":"xxxxx", "region":"us-east-1"} diff --git a/requirements.txt b/requirements.txt index 5ea1b659..02805f33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,6 @@ django-storages django-structlog django-test-without-migrations djangorestframework -dj_database_url drf-spectacular docutils dotenv From 39ed15b10fe6d8f60c6e60305dfe65a3b79155b1 Mon Sep 17 00:00:00 2001 From: Miles Wells Date: Fri, 24 Jul 2026 13:05:00 +0100 Subject: [PATCH 9/9] Bump version to 3.6.2 and update changelog --- CHANGELOG.md | 19 +++++++++++++++++++ alyx/alyx/__init__.py | 2 +- requirements.txt | 1 - 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b937c91..d0af2e21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.6.2] + +### Fixed + +- Scoped delete_zygosity_rule post delete hook, improving performance on delete + +### Changed + +- Removed unused dj_database_url dependency +- Omit WSGI from CI code coverage +- Changed default docker project name +- Use commit message for release description on squash commits to master +- Pin ruff and remove flake8 + +### Added + +- DOI badge in README +- Release steps to README + ## [3.6.1] ### Fixed diff --git a/alyx/alyx/__init__.py b/alyx/alyx/__init__.py index cc0eb430..a321af33 100644 --- a/alyx/alyx/__init__.py +++ b/alyx/alyx/__init__.py @@ -1 +1 @@ -VERSION = __version__ = '3.6.1' +VERSION = __version__ = '3.6.2' diff --git a/requirements.txt b/requirements.txt index 02805f33..642d0541 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,6 @@ drf-spectacular docutils dotenv drfdocs -flake8 fonttools>=4.61.0 globus-cli>=3.41.0 globus-sdk>=4.3.0