diff options
| author | Natalia <124304+nessita@users.noreply.github.com> | 2026-01-20 17:32:09 -0300 |
|---|---|---|
| committer | nessita <124304+nessita@users.noreply.github.com> | 2026-01-26 11:42:58 -0300 |
| commit | 29ef283090668efab79b8547750297c4071dca92 (patch) | |
| tree | ee281b064149b989014270df79b9be69b3606988 /releases | |
| parent | 1e23a7c2cd6cd2798df1d6ba7833b7bb53d5487d (diff) | |
Extended Release model with properties in preparation to incorporate the checklist-generator code.
Diffstat (limited to 'releases')
| -rw-r--r-- | releases/admin.py | 1 | ||||
| -rw-r--r-- | releases/migrations/0006_alter_release_date.py | 26 | ||||
| -rw-r--r-- | releases/models.py | 49 | ||||
| -rw-r--r-- | releases/tests.py | 155 |
4 files changed, 228 insertions, 3 deletions
diff --git a/releases/admin.py b/releases/admin.py index b12b3659..aae877a0 100644 --- a/releases/admin.py +++ b/releases/admin.py @@ -51,6 +51,7 @@ class ReleaseAdmin(admin.ModelAdmin): ) list_filter = ("status", "is_lts", "is_active") ordering = ("-major", "-minor", "-micro", "-status", "-iteration") + search_fields = ("version",) @admin.display( description="status", diff --git a/releases/migrations/0006_alter_release_date.py b/releases/migrations/0006_alter_release_date.py new file mode 100644 index 00000000..a2eedbcd --- /dev/null +++ b/releases/migrations/0006_alter_release_date.py @@ -0,0 +1,26 @@ +# Generated by Django 6.0 on 2026-01-20 14:31 + +import datetime + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("releases", "0005_release_artifacts"), + ] + + operations = [ + migrations.AlterField( + model_name="release", + name="date", + field=models.DateField( + blank=True, + default=datetime.date.today, + help_text="Leave blank if the release date isn't known yet, typically if you're creating the final release just after the alpha because you want to build docs for the upcoming version.", + null=True, + verbose_name="Release date", + ), + ), + ] diff --git a/releases/models.py b/releases/models.py index ac9bf577..e4f746d4 100644 --- a/releases/models.py +++ b/releases/models.py @@ -1,5 +1,6 @@ import datetime import re +from functools import total_ordering from pathlib import Path from django.conf import settings @@ -160,6 +161,7 @@ def upload_to_checksum(release, filename): return f"pgp/Django-{version}.checksum.txt" +@total_ordering class Release(models.Model): DEFAULT_CACHE_KEY = "%s_django_version" % settings.CACHE_MIDDLEWARE_KEY_PREFIX STATUS_CHOICES = ( @@ -189,7 +191,7 @@ class Release(models.Model): null=True, blank=True, default=datetime.date.today, - help_text="Leave blank if the release date isn't know yet, typically " + help_text="Leave blank if the release date isn't known yet, typically " "if you're creating the final release just after the alpha " "because you want to build docs for the upcoming version.", ) @@ -210,8 +212,8 @@ class Release(models.Model): is_lts = models.BooleanField( "Long Term Support", help_text=( - 'Is this a release for an <abbr title="Long Term Support">LTS</abbr> Django ' - "version (e.g. 5.2a1, 5.2, 5.2.4)?" + 'Is this a release for an <abbr title="Long Term Support">LTS</abbr> ' + "Django version (e.g. 5.2a1, 5.2, 5.2.4)?" ), default=False, ) @@ -273,6 +275,47 @@ class Release(models.Model): version.append(0) return tuple(version) + @cached_property + def version_verbose(self): + return ( + f"{self.feature_version} {self.get_status_display()} {self.iteration}" + if self.is_pre_release + else self.version + ) + + @cached_property + def feature_version(self): + return f"{self.major}.{self.minor}" + + @cached_property + def feature_release(self): + return Release.objects.get(version=self.feature_version) + + @cached_property + def series(self): + return f"{self.major}.x" + + @cached_property + def stable_branch(self): + return f"stable/{self.feature_version}.x" + + @cached_property + def commit_prefix(self): + return f"[{self.feature_version}.x]" + + @cached_property + def is_pre_release(self): + """Return True if this is an alpha, beta, or rc release.""" + return self.status != "f" + + @cached_property + def is_dot_zero(self): + """Return True if this is a final X.Y.0 release.""" + return self.status == "f" and self.micro == 0 + + def __lt__(self, other): + return self.version_tuple < other.version_tuple + def clean(self): if self.is_published and not self.tarball: raise ValidationError( diff --git a/releases/tests.py b/releases/tests.py index a3553d1f..a9170faa 100644 --- a/releases/tests.py +++ b/releases/tests.py @@ -245,6 +245,161 @@ class ReleaseTestCase(TestCase): self.assertIsNone(next_release.eol_date) self.assertIsNone(other_release.eol_date) + def test_version_tuple(self): + cases = [ + ("1.0", (1, 0, 0, "final", 0)), + ("1.8", (1, 8, 0, "final", 0)), + ("1.8.1", (1, 8, 1, "final", 0)), + ("1.8a1", (1, 8, 0, "alpha", 1)), + ("1.8b1", (1, 8, 0, "beta", 1)), + ("1.8rc1", (1, 8, 0, "rc", 1)), + ("5.2", (5, 2, 0, "final", 0)), + ("5.2a1", (5, 2, 0, "alpha", 1)), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertEqual(release.version_tuple, expected) + + def test_version_verbose(self): + cases = [ + ("5.2a1", "5.2 alpha 1"), + ("5.2a2", "5.2 alpha 2"), + ("5.2b1", "5.2 beta 1"), + ("5.2rc1", "5.2 release candidate 1"), + ("5.2", "5.2"), + ("5.2.1", "5.2.1"), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertEqual(release.version_verbose, expected) + + def test_feature_version(self): + cases = [ + ("5.2", "5.2"), + ("5.2a1", "5.2"), + ("5.2.1", "5.2"), + ("5.2.15", "5.2"), + ("4.1rc1", "4.1"), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertEqual(release.feature_version, expected) + + def test_feature_release(self): + feature = Release.objects.create(version="5.2") + + # Feature release itself should return itself. + self.assertEqual(feature.feature_release, feature) + self.assertEqual(feature.feature_release.version, "5.2") + + # All other versions in the series should return the feature release + cases = ["5.2a1", "5.2b1", "5.2rc1", "5.2.1", "5.2.2"] + for version in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertEqual(release.feature_release, feature) + self.assertEqual(release.feature_release.version, "5.2") + + def test_series(self): + cases = [ + ("5.2", "5.x"), + ("5.2.1", "5.x"), + ("4.1", "4.x"), + ("3.2rc1", "3.x"), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertEqual(release.series, expected) + + def test_stable_branch(self): + cases = [ + ("5.2", "stable/5.2.x"), + ("5.2.1", "stable/5.2.x"), + ("4.1rc1", "stable/4.1.x"), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertEqual(release.stable_branch, expected) + + def test_commit_prefix(self): + cases = [ + ("5.2", "[5.2.x]"), + ("5.2.1", "[5.2.x]"), + ("4.1rc1", "[4.1.x]"), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertEqual(release.commit_prefix, expected) + + def test_is_pre_release(self): + cases = [ + ("5.2a1", True), + ("5.2b1", True), + ("5.2rc1", True), + ("5.2", False), + ("5.2.1", False), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertIs(release.is_pre_release, expected) + + def test_is_dot_zero(self): + cases = [ + ("5.2", True), + ("4.1", True), + ("5.2.1", False), + ("5.2.15", False), + ("5.2a1", False), + ("5.2rc1", False), + ] + for version, expected in cases: + with self.subTest(version=version): + release = Release.objects.create(version=version) + self.assertIs(release.is_dot_zero, expected) + + def test_ordering(self): + r1 = Release.objects.create(version="5.2") + r2 = Release.objects.create(version="5.2.1") + r3 = Release.objects.create(version="6.0a1") + r4 = Release.objects.create(version="6.0") + + # Comparison. + self.assertLessEqual(r1, r1) + self.assertLess(r1, r2) + self.assertLess(r2, r3) + self.assertLess(r1, r3) + self.assertLess(r3, r4) + self.assertGreater(r2, r1) + self.assertGreaterEqual(r1, r1) + + # Sorting. + releases = [r3, r1, r2, r4] + self.assertEqual(sorted(releases), [r1, r2, r3, r4]) + + def test_equality(self): + r1 = Release(version="5.2") + r2 = Release(version="5.2") + + self.assertLessEqual(r1, r2) + self.assertLessEqual(r2, r1) + # If r1 <= r2 and r2 <= r1, then r1 == r2 should be True. + self.assertEqual(r1, r2) + + def test_release_hash(self): + r1 = Release.objects.create(version="5.2") + r2 = Release.objects.create(version="5.2.1") + + self.assertEqual({r1, r2, r1}, {r1, r2}) + self.assertNotEqual(hash(r1), hash(r2)) + self.assertEqual(hash(r1), hash(r1)) + class ReleaseUploadToTestCase(SimpleTestCase): def test_upload_to_artifact(self): |
