12 Commits

Author SHA1 Message Date
Jelle van der Waa
336d686ca2 public: add spacing between past donors and a past donor
Some checks failed
Github-Actions / build (push) Failing after 7s
2025-01-25 12:10:38 +01:00
Christian Heusel
4f0e24f1f7 donate: Add link to DotcomMonitor
Signed-off-by: Christian Heusel <christian@heusel.eu>
2025-01-25 11:55:13 +01:00
Jelle van der Waa
e07054c8ea .github: run ruff in CI
Some checks failed
Github-Actions / build (push) Failing after 56s
2025-01-18 16:17:17 +01:00
Jelle van der Waa
97aae09dce flake8: limit line length to 118
Apply the same limit as the ruff configuration. flake8 is still used as
the relevant E* rules are still in preview mode.
2025-01-18 16:17:17 +01:00
Jelle van der Waa
0ce1a0ea5f main: fix last remaining line length violation 2025-01-18 16:17:17 +01:00
Jelle van der Waa
f38770be76 ruff.toml: ignore cssmin code 2025-01-18 16:17:17 +01:00
Jelle van der Waa
2d39dc6379 bump Python version to 3.13 2025-01-18 16:17:17 +01:00
Jelle van der Waa
df4b0bfd67 Fix domain being None in opensearch results
The domain came from HTTP_HOST which in our nginx configuration is not
set, furthermore other code already uses Site to obtain the domain.

Closes: #541
2025-01-18 13:18:51 +01:00
Jelle van der Waa
5da7fa80c5 treewide: reduce line length to 118 2025-01-18 12:42:02 +01:00
Jelle van der Waa
8d495d4fa7 planet: reduce line length to 118 2025-01-18 12:42:02 +01:00
Jelle van der Waa
796c3f410f todolists: reduce line length to 118 2025-01-18 12:42:02 +01:00
dependabot[bot]
37687bf9e4 build(deps): bump django from 5.0.10 to 5.0.11
Bumps [django](https://github.com/django/django) from 5.0.10 to 5.0.11.
- [Commits](https://github.com/django/django/compare/5.0.10...5.0.11)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-15 09:48:13 +01:00
28 changed files with 93 additions and 41 deletions

View File

@@ -1,3 +1,3 @@
[flake8]
max-line-length = 300
max-line-length = 118
ignore = E731, E241, E741

View File

@@ -9,14 +9,17 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.11
- name: Set up Python 3.13
uses: actions/setup-python@v4
with:
python-version: 3.11
python-version: 3.13
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt && pip install -r requirements_test.txt
pip install -r requirements.txt && pip install -r requirements_test.txt && pip install ruff
- name: Run ruff
run: |
ruff check .
- name: Lint with flake8
run: |
make lint

View File

@@ -71,7 +71,8 @@ class Database(object):
retry = False
except OperationalError as exc:
retry_count += 1
logger.error('Unable to update database \'%s\', retrying=%d', self.path, retry_count, exc_info=exc)
logger.error('Unable to update database \'%s\', retrying=%d',
self.path, retry_count, exc_info=exc)
time.sleep(5)
if retry_count == self.retry_limit:

View File

@@ -89,7 +89,9 @@ class Command(BaseCommand):
for name in all_paths:
manager.add_watch(name, mask)
handler = EventHandler(arch_paths=arch_path_map, filename_suffix='.links.tar.gz', callback_func=wrapper_read_links)
handler = EventHandler(arch_paths=arch_path_map,
filename_suffix='.links.tar.gz',
callback_func=wrapper_read_links)
return pyinotify.Notifier(manager, handler)

View File

@@ -539,7 +539,9 @@ def parse_info(pkgname, filename, iofile):
elif blockname:
store[blockname].append(line)
else:
raise Exception("%s: Read package info outside a block while reading from %s: %s" % (pkgname, filename, line))
raise Exception("%s: Read package info outside a block while reading from %s: %s" % (pkgname,
filename,
line))
return store

View File

@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import zoneinfo
from django.contrib.auth.models import Group, User
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models

View File

@@ -202,7 +202,8 @@ def non_existing_dependencies(packages):
def non_reproducible_packages(packages):
statuses = RebuilderdStatus.objects.select_related().filter(status=RebuilderdStatus.BAD, pkg__pkgname__in=packages.values('pkgname'))
statuses = RebuilderdStatus.objects.select_related().filter(status=RebuilderdStatus.BAD,
pkg__pkgname__in=packages.values('pkgname'))
return linkify_non_reproducible_packages(statuses)
@@ -227,7 +228,7 @@ def orphan_dependencies(packages):
JOIN packages_packagerelation ppr ON pp.pkgbase = ppr.pkgbase
JOIN (SELECT DISTINCT cp.pkgname FROM packages cp LEFT JOIN packages_packagerelation pr ON cp.pkgbase = pr.pkgbase WHERE pr.id IS NULL) child ON ppd.name = child.pkgname
ORDER BY child.pkgname;
"""
""" # noqa: E501
cursor.execute(query)
for row in cursor.fetchall():

View File

@@ -216,7 +216,9 @@ def tier0_mirror_auth(request):
token = credentials[1]
groups = Group.objects.filter(name__in=SELECTED_GROUPS)
user = User.objects.filter(username=username, is_active=True, groups__in=groups).select_related('userprofile').first()
user = User.objects.filter(username=username,
is_active=True,
groups__in=groups).select_related('userprofile').first()
if not user:
return unauthorized

View File

@@ -280,7 +280,8 @@ class Package(models.Model):
dep_pkgs = list(dep_pkgs)
dep = dep_pkgs[0]
if len(dep_pkgs) > 1:
dep_pkgs = [d for d in dep_pkgs if d.pkg.repo.testing == self.repo.testing and d.pkg.repo.staging == self.repo.staging]
dep_pkgs = [d for d in dep_pkgs
if d.pkg.repo.testing == self.repo.testing and d.pkg.repo.staging == self.repo.staging]
if len(dep_pkgs) > 0:
dep = dep_pkgs[0]
trimmed.append(dep)

View File

@@ -1,9 +1,10 @@
import cssmin
import jsmin
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
from django.core.files.base import ContentFile
from django.utils.encoding import smart_str
import cssmin
class MinifiedStaticFilesStorage(ManifestStaticFilesStorage):
"""

View File

@@ -35,7 +35,7 @@ def pgp_dev_key_link(key_id):
key_id = pad_key_id(key_id)
if not key_id:
return "Unknown"
link_text = (''.join((f'<span>{key_id[i:i+4]}</span>' for i in range(0, len(key_id), 4))))
link_text = (''.join((f'<span>{key_id[i:i + 4]}</span>' for i in range(0, len(key_id), 4))))
link_text = f'<div class="pgp-key-ids">{link_text}</div>'
return pgp_key_link(key_id, link_text)
@@ -51,7 +51,9 @@ def pgp_key_link(key_id, link_text=None):
return format_key(key_id)
pgp_server_secure = getattr(settings, 'PGP_SERVER_SECURE', False)
scheme = 'https' if pgp_server_secure else 'http'
url = '%s://%s/pks/lookup?op=vindex&amp;fingerprint=on&amp;exact=on&amp;search=0x%s' % (scheme, pgp_server, key_id)
url = '%s://%s/pks/lookup?op=vindex&amp;fingerprint=on&amp;exact=on&amp;search=0x%s' % (scheme,
pgp_server,
key_id)
if link_text is None:
link_text = '0x%s' % key_id[-8:]
values = (url, format_key(key_id), link_text)

View File

@@ -54,10 +54,12 @@ class NewsCreateView(CreateView):
if settings.MAILMAN_PASSWORD:
headers['Approved'] = settings.MAILMAN_PASSWORD
template = loader.get_template('news/news_email_notification.txt')
author = newsitem.author.get_full_name()
from_ = f'"Arch Linux: Recent news updates: {author}" <{settings.ANNOUNCE_EMAIL}>'
EmailMessage(
subject=f'[arch-announce] {newsitem.title}',
body=template.render(ctx),
from_email=f'"Arch Linux: Recent news updates: {newsitem.author.get_full_name()}" <{settings.ANNOUNCE_EMAIL}>',
from_email=from_,
to=[settings.ANNOUNCE_EMAIL],
headers=headers).send()
return super(NewsCreateView, self).form_valid(form)

View File

@@ -53,7 +53,8 @@ def create_specification(package, log, finder):
def get_tag_info(repo, pkgbase, version):
# Gitlab requires the path to the gitlab repo to be html encoded and project name encoded in a different special way
# Gitlab requires the path to the gitlab repo to be html encoded and
# project name encoded in a different special way
pkgrepo = urllib.parse.quote_plus(f'{settings.GITLAB_PACKAGE_REPO}/') + gitlab_project_name_to_path(pkgbase)
url = f'https://{settings.GITLAB_INSTANCE}/api/v4/projects/{pkgrepo}/repository/tags'
@@ -112,7 +113,8 @@ def cleanup_signoff_comments():
id_signoffs = [signoff.id for g in groups for signoff in g.signoffs]
logger.info("Keeping %s signoffs", len(id_signoffs))
# FakeSignoffSpecification's have no id
id_signoffspecs = [g.specification.id for g in groups if not isinstance(g.specification, FakeSignoffSpecification)]
id_signoffspecs = [g.specification.id for g in groups if not isinstance(g.specification,
FakeSignoffSpecification)]
logger.info("Keeping %s signoffspecifications", len(id_signoffspecs))
Signoff.objects.exclude(id__in=id_signoffs).delete()

View File

@@ -253,7 +253,8 @@ class UpdateManager(models.Manager):
if new_pkg:
update.action_flag = CHANGE
# ensure we should even be logging this
if old_pkg.pkgver == new_pkg.pkgver and old_pkg.pkgrel == new_pkg.pkgrel and old_pkg.epoch == new_pkg.epoch:
if old_pkg.pkgver == new_pkg.pkgver and old_pkg.pkgrel == new_pkg.pkgrel \
and old_pkg.epoch == new_pkg.epoch:
# all relevant fields were the same; e.g. a force update
return
else:
@@ -395,7 +396,8 @@ class RelatedToBase(models.Model):
# actually satisfy the requirements
if self.comparison and self.version:
alpm = AlpmAPI()
pkgs = [pkg for pkg in pkgs if not alpm.available or alpm.compare_versions(pkg.full_version, self.comparison, self.version)]
pkgs = [pkg for pkg in pkgs if not alpm.available or alpm.compare_versions(pkg.full_version,
self.comparison, self.version)]
if len(pkgs) == 0:
# couldn't find a package in the DB
# it should be a virtual depend (or a removed package)

View File

@@ -67,6 +67,7 @@ def test_sort(client, package):
def test_packages(client, package):
response = client.get('/opensearch/packages/')
assert response.status_code == 200
assert 'template="example.com/opensearch/packages/"' in response.content.decode()
def test_packages_suggest(client, package):

View File

@@ -5,6 +5,7 @@ from collections import defaultdict
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.cache import cache
from django.db.models import Q
from django.http import HttpResponse, HttpResponseBadRequest
@@ -21,10 +22,10 @@ from ..utils import get_wrong_permissions, multilib_differences
@require_safe
@cache_control(public=True, max_age=86400)
def opensearch(request):
domain = "%s://%s" % (request.scheme, request.META.get('HTTP_HOST'))
current_site = Site.objects.get_current()
return render(request, 'packages/opensearch.xml',
{'domain': domain},
{'domain': current_site.domain},
content_type='application/opensearchdescription+xml')
@@ -140,8 +141,14 @@ def sonames(request):
name = request.GET.get('name')
if name:
sonames = Soname.objects.filter(name__startswith=name).values('pkg__pkgname', 'pkg__pkgver', 'pkg__pkgrel', 'pkg__epoch', 'pkg__repo__name')
packages = [{'pkgname': soname['pkg__pkgname'], 'pkgrel': soname['pkg__pkgrel'], 'pkgver': soname['pkg__pkgver'], 'epoch': soname['pkg__epoch'], 'repo': soname['pkg__repo__name'].lower()} for soname in sonames]
sonames = Soname.objects.filter(name__startswith=name).values('pkg__pkgname',
'pkg__pkgver',
'pkg__pkgrel',
'pkg__epoch',
'pkg__repo__name')
packages = [{'pkgname': soname['pkg__pkgname'], 'pkgrel': soname['pkg__pkgrel'],
'pkgver': soname['pkg__pkgver'], 'epoch': soname['pkg__epoch'],
'repo': soname['pkg__repo__name'].lower()} for soname in sonames]
else:
return HttpResponseBadRequest('name parameter is required')

View File

@@ -38,7 +38,8 @@ class FlagForm(forms.Form):
# make sure the message isn't garbage (only punctuation or whitespace)
# or spam (using a simple denylist)
# and ensure a certain minimum length
if re.match(r'^[^0-9A-Za-z]+$', data) or any(fd.keyword in data for fd in FlagDenylist.objects.all()) or len(data) < 3:
if re.match(r'^[^0-9A-Za-z]+$', data) or any(fd.keyword in data for fd in FlagDenylist.objects.all()) \
or len(data) < 3:
raise forms.ValidationError("Enter a valid and useful out-of-date message.")
return data

View File

@@ -50,7 +50,8 @@ class Migration(migrations.Migration):
('author', models.CharField(max_length=255)),
('publishdate', models.DateTimeField(db_index=True, verbose_name='publish date')),
('url', models.CharField(max_length=255, verbose_name='URL')),
('feed', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='feed', to='planet.Feed')),
('feed', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE,
related_name='feed', to='planet.Feed')),
],
options={
'verbose_name_plural': 'Feed Items',

View File

@@ -14,6 +14,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='feeditem',
name='feed',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='items', to='planet.feed'),
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE,
related_name='items', to='planet.feed'),
),
]

View File

@@ -3,6 +3,7 @@ from datetime import datetime
from operator import attrgetter
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.db.models import Count, Q
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
@@ -25,12 +26,12 @@ def index(request):
else:
def updates():
return get_recent_updates()
domain = "%s://%s" % (request.scheme, request.META.get('HTTP_HOST'))
current_site = Site.objects.get_current()
context = {
'news_updates': News.objects.order_by('-postdate', '-id')[:15],
'pkg_updates': updates,
'staff_groups': StaffGroup.objects.all(),
'domain': domain,
'domain': current_site.domain,
}
return render(request, 'public/index.html', context)
@@ -92,8 +93,9 @@ def feeds(request):
@cache_control(max_age=307)
def keys(request):
profile_ids = UserProfile.allowed_repos.through.objects.values('userprofile_id')
users = User.objects.filter(
is_active=True, userprofile__id__in=profile_ids).order_by('first_name', 'last_name').select_related('userprofile')
users = User.objects.filter(is_active=True,
userprofile__id__in=profile_ids).order_by('first_name',
'last_name').select_related('userprofile')
user_key_ids = frozenset(user.userprofile.pgp_key[-16:] for user in users
if user.userprofile.pgp_key)

View File

@@ -9,8 +9,10 @@ from .views import ReleaseDetailView, ReleaseListView
releases_patterns = [
path('', ReleaseListView.as_view(), name='releng-release-list'),
path('json/', views.releases_json, name='releng-release-list-json'),
re_path(r'^(?P<version>[-.\w]+)/$', cache_page(311)(ReleaseDetailView.as_view()), name='releng-release-detail'),
re_path(r'^(?P<version>[-.\w]+)/torrent/$', cache_page(311)(views.release_torrent), name='releng-release-torrent'),
re_path(r'^(?P<version>[-.\w]+)/$', cache_page(311)(ReleaseDetailView.as_view()),
name='releng-release-detail'),
re_path(r'^(?P<version>[-.\w]+)/torrent/$', cache_page(311)(views.release_torrent),
name='releng-release-torrent'),
]
netboot_patterns = [

View File

@@ -1,5 +1,5 @@
-e git+https://github.com/fredj/cssmin.git@master#egg=cssmin
Django==5.0.10
Django==5.0.11
IPy==1.1
Markdown==3.3.7
bencode.py==4.0.0

View File

@@ -24,7 +24,6 @@ select = [
]
ignore = [
"E501", # line lengt violation
"E731", # Do not assign a `lambda` expression, use a `def`
"B904", # Within an `except` clause, raise exceptions with `raise ... from err`
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
@@ -34,3 +33,8 @@ ignore = [
"DJ008", # Model does not define `__str__` method
"DJ012", # Order of model's inner classes, methods, and fields does not follow the Django Style Guide: `Meta` class should come before `get_absolute_url`
]
exclude = [
"*/migrations/*.py", # Ignore Django migrations
"src/cssmin/*" # cssmin, not our code
]

View File

@@ -266,7 +266,9 @@ if DEBUG_TOOLBAR:
INSTALLED_APPS = [*list(INSTALLED_APPS), 'debug_toolbar']
if PROMETHEUS_METRICS:
MIDDLEWARE = ['django_prometheus.middleware.PrometheusBeforeMiddleware', *list(MIDDLEWARE), 'django_prometheus.middleware.PrometheusAfterMiddleware']
MIDDLEWARE = ['django_prometheus.middleware.PrometheusBeforeMiddleware',
*list(MIDDLEWARE),
'django_prometheus.middleware.PrometheusAfterMiddleware']
INSTALLED_APPS = [*list(INSTALLED_APPS), 'django_prometheus']

View File

@@ -84,6 +84,8 @@
<h3>Past donors</h3>
<p><a href="http://www.dotcom-monitor.com/" title="Dotcom-Monitor">Dotcom-Monitor</a></p>
<div id="donor-list">
<ul>
{% for donor in donors %}

View File

@@ -28,7 +28,9 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(db_index=True)),
('last_modified', models.DateTimeField(editable=False)),
('raw', models.TextField(blank=True)),
('creator', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='created_todolists', to=settings.AUTH_USER_MODEL)),
('creator', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT,
related_name='created_todolists',
to=settings.AUTH_USER_MODEL)),
],
options={
'get_latest_by': 'created',
@@ -43,13 +45,16 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(editable=False)),
('last_modified', models.DateTimeField(editable=False)),
('removed', models.DateTimeField(blank=True, null=True)),
('status', models.SmallIntegerField(choices=[(0, 'Incomplete'), (1, 'Complete'), (2, 'In-progress')], default=0)),
('status', models.SmallIntegerField(choices=[(0, 'Incomplete'), (1, 'Complete'), (2, 'In-progress')],
default=0)),
('comments', models.TextField(blank=True, null=True)),
('arch', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.Arch')),
('pkg', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='main.Package')),
('pkg', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL,
to='main.Package')),
('repo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.Repo')),
('todolist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='todolists.Todolist')),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL,
to=settings.AUTH_USER_MODEL)),
],
options={
'get_latest_by': 'created',

View File

@@ -13,6 +13,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='todolist',
name='kind',
field=models.SmallIntegerField(choices=[(0, 'Rebuild'), (1, 'Task')], default=0, help_text='(Rebuild for soname bumps, Task for independent tasks)'),
field=models.SmallIntegerField(choices=[(0, 'Rebuild'), (1, 'Task')], default=0,
help_text='(Rebuild for soname bumps, Task for independent tasks)'),
),
]

View File

@@ -23,7 +23,8 @@ class Todolist(models.Model):
description = models.TextField()
creator = models.ForeignKey(User, on_delete=models.PROTECT, related_name="created_todolists")
created = models.DateTimeField(db_index=True)
kind = models.SmallIntegerField(default=REBUILD, choices=KIND_CHOICES, help_text='(Rebuild for soname bumps, Task for independent tasks)')
kind = models.SmallIntegerField(default=REBUILD, choices=KIND_CHOICES,
help_text='(Rebuild for soname bumps, Task for independent tasks)')
last_modified = models.DateTimeField(editable=False)
raw = models.TextField(blank=True)