|
1
|
from pathlib import Path |
|
2
|
|
|
3
|
from django.db import models |
|
4
|
|
|
5
|
from core.models import ActiveManager, Tracking |
|
6
|
|
|
7
|
|
|
8
|
class FossilRepository(Tracking): |
|
9
|
"""Links a Project to its on-disk .fossil SQLite file.""" |
|
10
|
|
|
11
|
project = models.OneToOneField("projects.Project", on_delete=models.CASCADE, related_name="fossil_repo") |
|
12
|
filename = models.CharField(max_length=255, unique=True, help_text="Filename relative to FOSSIL_DATA_DIR") |
|
13
|
file_size_bytes = models.BigIntegerField(default=0) |
|
14
|
fossil_project_code = models.CharField(max_length=40, blank=True, default="") |
|
15
|
last_checkin_at = models.DateTimeField(null=True, blank=True) |
|
16
|
checkin_count = models.PositiveIntegerField(default=0) |
|
17
|
|
|
18
|
# Remote sync |
|
19
|
remote_url = models.URLField(blank=True, default="", help_text="Upstream remote URL for sync") |
|
20
|
last_sync_at = models.DateTimeField(null=True, blank=True) |
|
21
|
upstream_artifacts_available = models.PositiveIntegerField(default=0, help_text="New artifacts available from upstream") |
|
22
|
|
|
23
|
# S3 tracking |
|
24
|
s3_key = models.CharField(max_length=500, blank=True, default="") |
|
25
|
s3_last_replicated_at = models.DateTimeField(null=True, blank=True) |
|
26
|
|
|
27
|
objects = ActiveManager() |
|
28
|
all_objects = models.Manager() |
|
29
|
|
|
30
|
class Meta: |
|
31
|
ordering = ["filename"] |
|
32
|
verbose_name = "Fossil Repository" |
|
33
|
verbose_name_plural = "Fossil Repositories" |
|
34
|
|
|
35
|
def __str__(self): |
|
36
|
return self.filename |
|
37
|
|
|
38
|
@property |
|
39
|
def full_path(self) -> Path: |
|
40
|
from constance import config |
|
41
|
|
|
42
|
return Path(config.FOSSIL_DATA_DIR) / self.filename |
|
43
|
|
|
44
|
@property |
|
45
|
def exists_on_disk(self) -> bool: |
|
46
|
return self.full_path.exists() |
|
47
|
|
|
48
|
|
|
49
|
class FossilSnapshot(Tracking): |
|
50
|
"""Binary snapshot of a .fossil file stored via Django's file storage.""" |
|
51
|
|
|
52
|
repository = models.ForeignKey(FossilRepository, on_delete=models.CASCADE, related_name="snapshots") |
|
53
|
file = models.FileField(upload_to="fossil_snapshots/%Y/%m/") |
|
54
|
file_size_bytes = models.BigIntegerField(default=0) |
|
55
|
fossil_hash = models.CharField(max_length=64, blank=True, default="", help_text="SHA-256 of the .fossil file") |
|
56
|
note = models.CharField(max_length=200, blank=True, default="") |
|
57
|
|
|
58
|
objects = ActiveManager() |
|
59
|
all_objects = models.Manager() |
|
60
|
|
|
61
|
class Meta: |
|
62
|
ordering = ["-created_at"] |
|
63
|
get_latest_by = "created_at" |
|
64
|
|
|
65
|
def __str__(self): |
|
66
|
return f"{self.repository.filename} @ {self.created_at:%Y-%m-%d %H:%M}" if self.created_at else self.repository.filename |
|
67
|
|
|
68
|
|
|
69
|
# Import related models so they're discoverable by Django |
|
70
|
from fossil.agent_claims import TicketClaim # noqa: E402, F401 |
|
71
|
from fossil.api_tokens import APIToken # noqa: E402, F401 |
|
72
|
from fossil.branch_protection import BranchProtection # noqa: E402, F401 |
|
73
|
from fossil.ci import StatusCheck # noqa: E402, F401 |
|
74
|
from fossil.code_reviews import CodeReview, ReviewComment # noqa: E402, F401 |
|
75
|
from fossil.forum import ForumPost # noqa: E402, F401 |
|
76
|
from fossil.notifications import Notification, NotificationPreference, ProjectWatch # noqa: E402, F401 |
|
77
|
from fossil.releases import Release, ReleaseAsset # noqa: E402, F401 |
|
78
|
from fossil.sync_models import GitMirror, SSHKey, SyncLog # noqa: E402, F401 |
|
79
|
from fossil.ticket_fields import TicketFieldDefinition # noqa: E402, F401 |
|
80
|
from fossil.ticket_reports import TicketReport # noqa: E402, F401 |
|
81
|
from fossil.user_keys import UserSSHKey # noqa: E402, F401 |
|
82
|
from fossil.webhooks import Webhook, WebhookDelivery # noqa: E402, F401 |
|
83
|
from fossil.workspaces import AgentWorkspace # noqa: E402, F401 |
|
84
|
|