Skip to content

Commit 8be9371

Browse files
committed
devel: import bumpbuddy out of date information
1 parent 459a71d commit 8be9371

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/usr/bin/python
2+
3+
"""
4+
read_bumpbuddy_status
5+
6+
Usage: ./manage.py read_bumpbuddy_status
7+
"""
8+
9+
10+
import logging
11+
from typing import NotRequired, TypedDict
12+
from urllib.parse import quote as urlquote
13+
14+
import requests
15+
from django.conf import settings
16+
from django.core.cache import cache
17+
from django.core.management.base import BaseCommand
18+
from django.utils.timezone import now
19+
20+
from main.models import Package
21+
from main.utils import gitlab_project_name_to_path
22+
from packages.alpm import AlpmAPI
23+
from packages.models import FlagRequest
24+
25+
logger = logging.getLogger("command")
26+
logger.setLevel(logging.WARNING)
27+
28+
29+
alpm = AlpmAPI()
30+
31+
32+
class PkgData(TypedDict):
33+
last_check: int
34+
local_version: str
35+
maintainers: list[str]
36+
message: str | None
37+
out_of_date: bool
38+
pkgbase: str
39+
static: str
40+
upstream_version: str
41+
issue: NotRequired[int]
42+
43+
44+
class Command(BaseCommand):
45+
def process_package(self, pkgdata: PkgData) -> FlagRequest | None:
46+
pkgbase = pkgdata['pkgbase']
47+
version = pkgdata['local_version']
48+
upstream_version = pkgdata['upstream_version']
49+
logger.debug("Import new out of date package '%s'", pkgbase)
50+
51+
packages = Package.objects.filter(pkgbase=pkgbase)
52+
found_packages = list(packages)
53+
54+
if len(found_packages) == 0:
55+
logger.error("no matching packages found for pkgbase='%s'", pkgbase)
56+
return
57+
58+
# already flagged
59+
not_flagged_packages = [pkg for pkg in found_packages if pkg.flag_date is None]
60+
if len(not_flagged_packages) == 0:
61+
return
62+
63+
ood_packages = [pkg for pkg in not_flagged_packages if alpm.vercmp(upstream_version, pkg.pkgver) > 0]
64+
if len(ood_packages) == 0:
65+
logger.debug("package is not out of date for pkgbase='%s'", pkgbase)
66+
return
67+
68+
pkg = ood_packages[0]
69+
70+
# find a common version if there is one available to store
71+
versions = {(pkg.pkgver, pkg.pkgrel, pkg.epoch) for pkg in ood_packages}
72+
if len(versions) == 1:
73+
version = versions.pop()
74+
else:
75+
version = ('', '', 0)
76+
77+
current_time = now()
78+
# Compatibility for old json output without issue
79+
if 'issue' in pkgdata:
80+
scm_pkgbase = urlquote(gitlab_project_name_to_path(pkgbase))
81+
issue_url = f"{settings.GITLAB_PACKAGES_REPO}/{scm_pkgbase}/-/issues/{pkgdata['issue']}"
82+
message = f"New version {pkgdata['upstream_version']} is available: {issue_url}"
83+
else:
84+
message = f"New version {pkgdata['upstream_version']} is available."
85+
86+
for pkg in ood_packages:
87+
pkg.flag_date = current_time
88+
pkg.save()
89+
90+
flag_request = FlagRequest(created=current_time,
91+
user_email="bumpbuddy@archlinux.org",
92+
message=message,
93+
ip_address="0.0.0.0",
94+
pkgbase=pkg.pkgbase,
95+
repo=pkg.repo,
96+
pkgver=version[0],
97+
pkgrel=version[1],
98+
epoch=version[2],
99+
num_packages=len(ood_packages))
100+
101+
return flag_request
102+
103+
def handle(self, *args, **options):
104+
v = int(options.get('verbosity', 0))
105+
if v == 0:
106+
logger.level = logging.ERROR
107+
elif v == 1:
108+
logger.level = logging.INFO
109+
elif v >= 2:
110+
logger.level = logging.DEBUG
111+
112+
url = getattr(settings, "BUMPBUDDY_URL", None)
113+
assert url is not None, "BUMPBUDDY_URL not configured"
114+
115+
headers = {}
116+
last_modified = cache.get('bumpbuddy:last-modified')
117+
if last_modified:
118+
logger.debug('Setting If-Modified-Since header')
119+
headers = {'If-Modified-Since': last_modified}
120+
121+
req = requests.get(url, headers)
122+
if req.status_code == 304:
123+
logger.debug('The rebuilderd data has not been updated since we last checked it')
124+
return
125+
126+
if req.status_code != 200:
127+
logger.error("Issues retrieving bumpbuddy data: '%s'", req.status_code)
128+
return
129+
130+
last_modified = req.headers.get('last-modified')
131+
if last_modified:
132+
cache.set('bumpbuddy:last-modified', last_modified, 3600) # cache one hour
133+
134+
flagged_packages = []
135+
for pkgdata in req.json().values():
136+
package = self.process_package(pkgdata)
137+
if package is not None:
138+
flagged_packages.append(package)
139+
140+
if flagged_packages:
141+
logger.info("Imported %d new out of date packages", len(flagged_packages))
142+
FlagRequest.objects.bulk_create(flagged_packages)
143+
144+
145+
# vim: set ts=4 sw=4 et:
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from typing import TYPE_CHECKING
2+
3+
import pytest
4+
from django.utils.timezone import now
5+
6+
from devel.management.commands.read_bumpbuddy_status import Command as BumpBuddyCommand
7+
from main.models import Arch, Package, Repo
8+
9+
if TYPE_CHECKING:
10+
from packages.models import FlagRequest
11+
12+
13+
@pytest.fixture
14+
def command():
15+
return BumpBuddyCommand()
16+
17+
18+
@pytest.fixture
19+
def package(arches, repos):
20+
arch = Arch.objects.get(name='x86_64')
21+
extra = Repo.objects.get(name='Extra')
22+
created = now()
23+
pkg = Package.objects.create(arch=arch, repo=extra,
24+
pkgname='systemd',
25+
pkgbase='systemd', pkgver=100,
26+
pkgrel=1, pkgdesc='Linux kernel',
27+
compressed_size=10, installed_size=20,
28+
last_update=created, created=created)
29+
30+
yield pkg
31+
pkg.delete()
32+
33+
34+
def test_not_outofdate(command, package):
35+
request = command.process_package({
36+
'pkgbase': 'systemd',
37+
'local_version': 100,
38+
'upstream_version': 100,
39+
'out_of_date': False,
40+
'issue': 12
41+
})
42+
43+
assert request is None
44+
45+
46+
def test_outofdate(command, package):
47+
request = command.process_package({
48+
'pkgbase': 'systemd',
49+
'local_version': 100,
50+
'upstream_version': 101,
51+
'out_of_date': True,
52+
'issue': 12
53+
})
54+
55+
assert request is not None
56+
57+
58+
def test_already_flagged(command, package):
59+
request: FlagRequest = command.process_package({
60+
'pkgbase': 'systemd',
61+
'local_version': 100,
62+
'upstream_version': 101,
63+
'out_of_date': True,
64+
'issue': 12
65+
})
66+
67+
assert request is not None
68+
assert request.pkgbase == 'systemd'
69+
assert request.pkgver == '100'
70+
assert str(request.message).startswith('New version 101 is available')
71+
request.save()
72+
73+
new_request = command.process_package({
74+
'pkgbase': 'systemd',
75+
'local_version': 100,
76+
'upstream_version': 101,
77+
'out_of_date': True,
78+
'issue': 12
79+
})
80+
assert new_request is None
81+
82+
request.delete()

settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,9 @@
226226
# Rebuilderd API endpoint
227227
REBUILDERD_URL = 'https://reproducible.archlinux.org/api/v0/pkgs/list'
228228

229+
# Bumpbuddy json endpoint
230+
BUMPBUDDY_URL = "https://bumpbuddy.archlinux.org/data.json"
231+
229232
# Protected TIER0 Mirror
230233
TIER0_MIRROR_DOMAIN = 'repos.archlinux.org'
231234
# TIER0_MIRROR_SECRET = ''

0 commit comments

Comments
 (0)