Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pushlog: add basic models (bug 1940610) #196

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ testpaths = [
"src/lando/api",
"src/lando/dockerflow",
"src/lando/main/tests",
"src/lando/pushlog/tests",
"src/lando/tests",
"src/lando/ui/tests",
]
11 changes: 11 additions & 0 deletions src/lando/main/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
RevisionLandingJob,
Worker,
)
from lando.pushlog.models import (
Commit,
File,
Push,
Tag,
)

admin.site.site_title = gettext_lazy("Lando Admin")
admin.site.site_header = gettext_lazy("Lando Administration")
Expand Down Expand Up @@ -43,3 +49,8 @@ class LandingJobAdmin(admin.ModelAdmin):
admin.site.register(Repo, admin.ModelAdmin)
admin.site.register(Worker, admin.ModelAdmin)
admin.site.register(ConfigurationVariable, admin.ModelAdmin)

admin.site.register(Push, admin.ModelAdmin)
admin.site.register(Commit, admin.ModelAdmin)
admin.site.register(File, admin.ModelAdmin)
admin.site.register(Tag, admin.ModelAdmin)
140 changes: 140 additions & 0 deletions src/lando/pushlog/migrations/0001_initial_updated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Generated by Django 5.1.4 on 2025-01-17 05:32

import django.core.validators
import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
("main", "0013_alter_repo_scm_type_alter_worker_scm"),
("main", "0013_alter_repo_scm_type_alter_worker_scm"),
("main", "0013_alter_repo_scm_type_alter_worker_scm"),
]

operations = [
migrations.CreateModel(
name="File",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=4096)),
(
"repo",
models.ForeignKey(
on_delete=django.db.models.deletion.DO_NOTHING, to="main.repo"
),
),
],
options={
"unique_together": {("repo", "name")},
},
),
migrations.CreateModel(
name="Commit",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"hash",
models.CharField(
db_index=True,
max_length=160,
validators=[
django.core.validators.MaxLengthValidator(160),
django.core.validators.MinLengthValidator(160),
django.core.validators.RegexValidator("^([a-fA-F0-9])+"),
],
),
),
(
"repo",
models.ForeignKey(
on_delete=django.db.models.deletion.DO_NOTHING, to="main.repo"
),
),
("author", models.CharField(max_length=512)),
("desc", models.TextField()),
("parents", models.ManyToManyField(blank=True, to="pushlog.commit")),
("files", models.ManyToManyField(to="pushlog.file")),
],
options={
"unique_together": {("repo", "hash")},
},
),
migrations.CreateModel(
name="Push",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("push_id", models.PositiveIntegerField()),
(
"repo",
models.ForeignKey(
on_delete=django.db.models.deletion.DO_NOTHING, to="main.repo"
),
),
("date", models.DateField(auto_now_add=True)),
("user", models.EmailField(max_length=320)),
("commits", models.ManyToManyField(to="pushlog.commit")),
("branch", models.CharField(max_length=255)),
],
options={
"unique_together": {("push_id", "repo")},
},
),
migrations.CreateModel(
name="Tag",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255)),
(
"repo",
models.ForeignKey(
on_delete=django.db.models.deletion.DO_NOTHING, to="main.repo"
),
),
(
"commit",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="pushlog.commit"
),
),
],
options={
"unique_together": {("repo", "name")},
},
),
]
Empty file.
9 changes: 9 additions & 0 deletions src/lando/pushlog/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .commit import Commit, File, Tag
from .push import Push

__all__ = [
"Commit",
"File",
"Push",
"Tag",
]
75 changes: 75 additions & 0 deletions src/lando/pushlog/models/commit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from django.core.validators import (
MaxLengthValidator,
MinLengthValidator,
RegexValidator,
)
from django.db import models

from lando.main.models import Repo

from .consts import COMMIT_ID_HEX_LENGTH, MAX_FILENAME_LENGTH, MAX_PATH_LENGTH


class File(models.Model):
name = models.CharField(max_length=MAX_PATH_LENGTH)

repo = models.ForeignKey(
Repo,
# We don't want to delete the PushLog, even if we were to delete the repo
# object.
on_delete=models.DO_NOTHING,
)

class Meta:
unique_together = ("repo", "name")


class Commit(models.Model):
hash = models.CharField(
max_length=COMMIT_ID_HEX_LENGTH,
db_index=True,
blank=False,
validators=[
MaxLengthValidator(COMMIT_ID_HEX_LENGTH),
MinLengthValidator(COMMIT_ID_HEX_LENGTH),
RegexValidator(r"^([a-fA-F0-9])+"),
],
)

repo = models.ForeignKey(
Repo,
# We don't want to delete the PushLog, even if we were to delete the repo
# object.
on_delete=models.DO_NOTHING,
)

# Assuming a max email address length (see Push model), and then some space for a long name.
author = models.CharField(max_length=512)

desc = models.TextField()

files = models.ManyToManyField(File)

parents = models.ManyToManyField("self", blank=True)

class Meta:
unique_together = ("repo", "hash")

def __repr__(self):
return f"<{self.__class__.__name__}({self.hash})>"


class Tag(models.Model):
# Tag names are limited by how long a filename the filesystem support.
name = models.CharField(max_length=MAX_FILENAME_LENGTH)
commit = models.ForeignKey(Commit, on_delete=models.CASCADE)

repo = models.ForeignKey(
Repo,
# We don't want to delete the PushLog, even if we were to delete the repo
# object.
on_delete=models.DO_NOTHING,
)

class Meta:
unique_together = ("repo", "name")
5 changes: 5 additions & 0 deletions src/lando/pushlog/models/consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
COMMIT_ID_HEX_LENGTH = 160

# Those are fairly common values under Unix.
MAX_FILENAME_LENGTH = 255
MAX_PATH_LENGTH = 4096
63 changes: 63 additions & 0 deletions src/lando/pushlog/models/push.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from django.db import models

from lando.main.models import Repo

from .commit import Commit
from .consts import MAX_FILENAME_LENGTH

PUSH_SCM_TYPE_GIT = "git"
PUSH_SCM_TYPES = [PUSH_SCM_TYPE_GIT]


class Push(models.Model):
push_id = models.PositiveIntegerField()

repo = models.ForeignKey(
Repo,
# We don't want to delete the PushLog, even if we were to delete the repo
# object.
on_delete=models.DO_NOTHING,
)

date = models.DateField(
auto_now=False,
auto_now_add=True,
)

# Maximum total lengths are defined in RFC-5321 [0]: 64 for the local-part, and 255
# for the domain.
# [0] https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1.1
user = models.EmailField(max_length=64 + 1 + 255)

commits = models.ManyToManyField(Commit)

# Branch names are limited by how long a filename the filesystem support. This is
# generally 255 bytes.
branch = models.CharField(max_length=MAX_FILENAME_LENGTH)

class Meta:
unique_together = ("push_id", "repo")

def __repr__(self):
return f"<{self.__class__.__name__}({self.push_id } on {self.repo})>"

def save(self, *args, **kwargs):
if not self.id:
# Determine the next push_id on first save
next_push_id = self._next_push_id(self.repo)
self.push_id = next_push_id
super(Push, self).save(*args, **kwargs)

@classmethod
def _next_push_id(cls, repo: Repo):
"""Generate a monotonically increasing sequence of push_id, scoped by Repo."""
max_push_id = (
cls.objects.filter(repo=repo)
.order_by("-push_id")
.values_list("push_id", flat=True)
)

if max_push_id:
return max_push_id[0] + 1

return 1
Loading
Loading