Yesterday's work: releases and ordering

This commit is contained in:
Klaas van Schelven
2023-12-01 08:18:32 +01:00
parent e4ef92ced5
commit 351d81b106
9 changed files with 136 additions and 0 deletions

View File

@@ -37,6 +37,7 @@ INSTALLED_APPS = [
'compat',
'projects',
'releases',
'ingest',
'issues',
'events',

0
releases/__init__.py Normal file
View File

3
releases/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
releases/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ReleasesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'releases'

View File

@@ -0,0 +1,30 @@
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('projects', '0002_project_name_project_sentry_key'),
]
operations = [
migrations.CreateModel(
name='Release',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('version', models.CharField(max_length=255)),
('date_released', models.DateTimeField(default=django.utils.timezone.now)),
('is_semver', models.BooleanField()),
('sort_epoch', models.IntegerField()),
('project', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='projects.project')),
],
options={
'unique_together': {('project', 'version')},
},
),
]

View File

60
releases/models.py Normal file
View File

@@ -0,0 +1,60 @@
import uuid
from semver.version import Version
from django.db import models
from django.utils import timezone
def is_valid_semver(version):
try:
Version.parse(version)
return True
except ValueError:
return False
def sort_key(release):
return (
release.sort_epoch,
Version.parse(release.version) if release.is_semver else release.date_released
)
def ordered_releases(*filter_args, **filter_kwargs):
"""..."""
releases = Release.objects.filter(*filter_args, **filter_kwargs)
return sorted(releases, key=sort_key)
class Release(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
project = models.ForeignKey(
"projects.Project", blank=False, null=True, on_delete=models.SET_NULL) # SET_NULL: cleanup 'later'
version = models.CharField(max_length=255, null=False, blank=False)
date_released = models.DateTimeField(default=timezone.now)
is_semver = models.BooleanField()
sort_epoch = models.IntegerField()
def save(self, *args, **kwargs):
if self.is_semver is None:
self.is_semver = is_valid_semver(self.version)
# whether doing this epoch setting inline on-creation is a smart idea... will become clear soon enough.
any_release_from_last_epoch = Release.filter(project=self.project).objects.order_by("sort_epoch").last()
if any_release_from_last_epoch is None:
self.sort_epoch = 0
elif self.is_semver == any_release_from_last_epoch.is_semver:
self.sort_epoch = any_release_from_last_epoch.sort_epoch
else:
self.sort_epoch = any_release_from_last_epoch.sort_epoch + 1
super(Release, self).save(*args, **kwargs)
class Meta:
unique_together = ("project", "version")

33
releases/tests.py Normal file
View File

@@ -0,0 +1,33 @@
from django.test import TestCase
from datetime import timedelta
from .models import Release, ordered_releases
class ReleaseTestCase(TestCase):
def test_create_and_order(self):
r0 = Release.objects.create(version="e80f98923f7426a8087009f4c629d25a35565a6a")
self.assertFalse(r0.is_semver)
self.assertEquals(0, r0.sort_epoch)
# still using hash; stay at epoch 0
# the timedelta avoids tests breaking when fast & checks dates are ignored when comparing against semver
r1 = Release.objects.create(
version="2a678dbbbecd2978ccaa76c326a0fb2e70073582",
date_released=r0.date_released + timedelta(seconds=10),
)
self.assertFalse(r1.is_semver)
self.assertEquals(0, r1.sort_epoch)
# switch to semver, epoch 1
r2 = Release.objects.create(version="1.0.0")
self.assertTrue(r2.is_semver)
self.assertEquals(1, r2.sort_epoch)
# stick with semver, but use a lower version
r3 = Release.objects.create(version="0.1.0")
self.assertTrue(r3.is_semver)
self.assertEquals(1, r3.sort_epoch)
self.assertEquals(ordered_releases(), [r0, r1, r3, r2])

3
releases/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.