Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
fb9b56c
fix tag handling
furlongm Oct 21, 2025
cc14aea
Merge pull request #710 from furlongm/tags
furlongm Oct 30, 2025
2bcd4da
fix package filter list for errata
furlongm Oct 30, 2025
856f41f
add support for zstd compression in deb and rpm repos
furlongm Oct 30, 2025
7045e1b
Merge pull request #708 from furlongm/zstd-support
furlongm Oct 30, 2025
0c53c82
Merge pull request #713 from furlongm/package-errata-fix
furlongm Oct 30, 2025
8d9da89
simplify logging
furlongm Oct 30, 2025
dc68147
Merge pull request #714 from furlongm/logging
furlongm Oct 30, 2025
00fbd6e
use redis for caching and use locks for tasks
furlongm Oct 30, 2025
fd6f9aa
add errata source options to config file
furlongm Apr 18, 2025
60a80cd
Merge pull request #716 from furlongm/errata-source-options
furlongm Oct 30, 2025
8fa3eb2
Merge pull request #715 from furlongm/redis-caching
furlongm Oct 30, 2025
f06729e
remove daily cronjob in favour of patchman-celery
furlongm May 15, 2025
15e5ba1
Merge pull request #717 from furlongm/cronjob
furlongm Oct 30, 2025
a59a23b
add isort check
furlongm Oct 31, 2025
c13a564
Merge pull request #718 from furlongm/isort
furlongm Oct 31, 2025
5fa1ef0
Bump django from 4.2.25 to 4.2.26
dependabot[bot] Nov 6, 2025
c8cf5e0
Merge pull request #719 from furlongm/dependabot/pip/django-4.2.26
furlongm Nov 6, 2025
aac552d
Bump django from 4.2.26 to 4.2.27
dependabot[bot] Dec 3, 2025
ffa815b
Merge pull request #720 from furlongm/dependabot/pip/django-4.2.27
furlongm Dec 10, 2025
da1f44e
Modified tag handling to preserve case
RicardoJeronimo Dec 20, 2025
d8f3ce4
fix same module in different repos
furlongm Dec 17, 2025
2e215a1
Merge pull request #723 from RicardoJeronimo/tags
furlongm Jan 1, 2026
95a5213
Merge pull request #725 from furlongm/modules-fix
furlongm Jan 1, 2026
65e8851
add priority queues for tasks (#724)
furlongm Jan 1, 2026
0338987
update celery services handling (#726)
furlongm Jan 8, 2026
ff7f293
remove non-present middleware (#729)
furlongm Jan 9, 2026
5e2bb76
fix wsgi so rpm module is only loaded once (#728)
furlongm Jan 9, 2026
33519f2
give systemd units usable defaults (#727)
furlongm Jan 9, 2026
6e9b21f
use consistent users/groups on rhel/debian (#730)
furlongm Jan 9, 2026
d450af7
fixes for dumping/loading fixtures from sqlite (#731)
furlongm Jan 10, 2026
f798882
update logging to log to console and celery systemd units (#732)
furlongm Jan 10, 2026
dc12ee1
Merge remote-tracking branch 'upstream/main' into docker
RicardoJeronimo Jan 15, 2026
aa43a63
Changed entrypoint script to adapt changes made in 8fa3eb2
RicardoJeronimo Jan 15, 2026
47afc1f
Merge branch 'docker' into patchman-email
RicardoJeronimo Jan 20, 2026
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
4 changes: 4 additions & 0 deletions .github/workflows/lint-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jobs:
pip install flake8
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
flake8 . --count --max-line-length=120 --show-source --statistics
- name: Check isort
run: |
pip install isort
isort --check --profile=django .
- name: Set secret key
run: ./sbin/patchman-set-secret-key
- name: Test with django
Expand Down
3 changes: 2 additions & 1 deletion arch/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from django.contrib import admin
from arch.models import PackageArchitecture, MachineArchitecture

from arch.models import MachineArchitecture, PackageArchitecture

admin.site.register(PackageArchitecture)
admin.site.register(MachineArchitecture)
2 changes: 1 addition & 1 deletion arch/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from rest_framework import serializers

from arch.models import PackageArchitecture, MachineArchitecture
from arch.models import MachineArchitecture, PackageArchitecture


class PackageArchitectureSerializer(serializers.HyperlinkedModelSerializer):
Expand Down
12 changes: 6 additions & 6 deletions arch/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from arch.models import PackageArchitecture, MachineArchitecture
from patchman.signals import info_message
from arch.models import MachineArchitecture, PackageArchitecture
from util.logging import info_message


def clean_package_architectures():
Expand All @@ -24,9 +24,9 @@ def clean_package_architectures():
parches = PackageArchitecture.objects.filter(package__isnull=True)
plen = parches.count()
if plen == 0:
info_message.send(sender=None, text='No orphaned PackageArchitectures found.')
info_message(text='No orphaned PackageArchitectures found.')
else:
info_message.send(sender=None, text=f'Removing {plen} orphaned PackageArchitectures')
info_message(text=f'Removing {plen} orphaned PackageArchitectures')
parches.delete()


Expand All @@ -39,9 +39,9 @@ def clean_machine_architectures():
)
mlen = marches.count()
if mlen == 0:
info_message.send(sender=None, text='No orphaned MachineArchitectures found.')
info_message(text='No orphaned MachineArchitectures found.')
else:
info_message.send(sender=None, text=f'Removing {mlen} orphaned MachineArchitectures')
info_message(text=f'Removing {mlen} orphaned MachineArchitectures')
marches.delete()


Expand Down
7 changes: 4 additions & 3 deletions arch/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@

from rest_framework import viewsets

from arch.models import PackageArchitecture, MachineArchitecture
from arch.serializers import PackageArchitectureSerializer, \
MachineArchitectureSerializer
from arch.models import MachineArchitecture, PackageArchitecture
from arch.serializers import (
MachineArchitectureSerializer, PackageArchitectureSerializer,
)


class PackageArchitectureViewSet(viewsets.ModelViewSet):
Expand Down
2 changes: 1 addition & 1 deletion debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Depends: ${misc:Depends}, python3 (>= 3.11), python3-django (>= 4.2),
python3-requests, python3-colorama, python3-magic, python3-humanize,
python3-yaml, libapache2-mod-wsgi-py3, apache2, sqlite3,
celery, python3-celery, python3-django-celery-beat, redis-server,
python3-redis, python3-git, python3-django-taggit
python3-redis, python3-git, python3-django-taggit, python3-zstandard
Suggests: python3-mysqldb, python3-psycopg2, python3-pymemcache, memcached
Description: Django-based patch status monitoring tool for linux systems.
.
Expand Down
3 changes: 0 additions & 3 deletions debian/python3-patchman.cron.daily

This file was deleted.

3 changes: 2 additions & 1 deletion debian/python3-patchman.install
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/dh-exec
etc/patchman/apache.conf.example => etc/apache2/conf-available/patchman.conf
etc/patchman/local_settings.py etc/patchman
etc/systemd/system/patchman-celery.service => lib/systemd/system/patchman-celery.service
etc/systemd/system/patchman-celery-worker.service => lib/systemd/system/patchman-celery-worker@.service
etc/systemd/system/patchman-celery-beat.service => lib/systemd/system/patchman-celery-beat.service
35 changes: 32 additions & 3 deletions debian/python3-patchman.postinst
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,39 @@ if [ "$1" = "configure" ] ; then
patchman-manage migrate --run-syncdb --fake-initial
sqlite3 /var/lib/patchman/db/patchman.db 'PRAGMA journal_mode=WAL;'

chown -R www-data:www-data /var/lib/patchman
adduser --system --group patchman-celery
usermod -a -G www-data patchman-celery
adduser --quiet --system --group patchman
adduser --quiet www-data patchman
chown root:patchman /etc/patchman/celery.conf
chmod 640 /etc/patchman/celery.conf
chmod g+w /var/lib/patchman /var/lib/patchman/db /var/lib/patchman/db/patchman.db
chown -R patchman:patchman /var/lib/patchman

WORKER_COUNT=1
if [ -f /etc/patchman/celery.conf ]; then
. /etc/patchman/celery.conf
WORKER_COUNT=${CELERY_WORKER_COUNT:-1}
fi

if [ -d /run/systemd/system ]; then
systemctl daemon-reload >/dev/null || true
for i in $(seq 1 "${WORKER_COUNT}"); do
deb-systemd-helper enable "patchman-celery-worker@$i.service" >/dev/null || true
deb-systemd-invoke start "patchman-celery-worker@$i.service" >/dev/null || true
done

active_instances=$(systemctl list-units --type=service --state=active "patchman-celery-worker@*" --no-legend | awk '{print $1}')

for service in $active_instances; do
inst_num=$(echo "$service" | cut -d'@' -f2 | cut -d'.' -f1)
if [ "$inst_num" -gt "${WORKER_COUNT}" ]; then
deb-systemd-invoke stop "$service" >/dev/null || true
deb-systemd-helper disable "$service" >/dev/null || true
fi
done

deb-systemd-helper enable "patchman-celery-beat.service" >/dev/null || true
deb-systemd-invoke start "patchman-celery-beat.service" >/dev/null || true
fi

echo
echo "Remember to run 'patchman-manage createsuperuser' to create a user."
Expand Down
15 changes: 7 additions & 8 deletions docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,16 @@ if "${USE_CACHE}"; then
redisPort="6379"
fi

# Comment DummyCache Block
sed -i '47,51 {/^#/ ! s/\(.*\)/#\1/}' "$conf"

# Uncomment RedisCache Block
sed -i '55,61 {s/^# //}' "$conf"

sed -i "58 {s/127.0.0.1:6379/$redisHost:$redisPort/}" "$conf"
# Change RedisCache LOCATION
sed -i "62 {s/127.0.0.1:6379/$redisHost:$redisPort/}" "$conf"

if [ -n "${CACHE_TIMEOUT}" ]; then
sed -i "59 {s/30/${CACHE_TIMEOUT}/}" "$conf"
sed -i "67 {s/0/${CACHE_TIMEOUT}/}" "$conf"
fi
else
# Change RedisCache to DummyCache to avoid ConnectionError and comment LOCATION
sed -i '61 {s/redis.RedisCache/dummy.DummyCache/}' "$conf"
sed -i '62 {/^#/ ! s/\(.*\)/#\1/}' "$conf"
fi

# Set sendmail destination
Expand Down
1 change: 1 addition & 0 deletions domains/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from django.contrib import admin

from domains.models import Domain

admin.site.register(Domain)
1 change: 1 addition & 0 deletions errata/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

from django.contrib import admin

from errata.models import Erratum


Expand Down
17 changes: 8 additions & 9 deletions errata/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@

import json

from django.db import models
from django.db import IntegrityError, models
from django.urls import reverse
from django.db import IntegrityError

from errata.managers import ErratumManager
from packages.models import Package, PackageUpdate
from packages.utils import find_evr, get_matching_packages
from errata.managers import ErratumManager
from security.models import CVE, Reference
from security.utils import get_or_create_cve, get_or_create_reference
from patchman.signals import error_message
from util import get_url
from util.logging import error_message


class Erratum(models.Model):
Expand Down Expand Up @@ -70,7 +69,7 @@ def scan_for_security_updates(self):
try:
affected_update.save()
except IntegrityError as e:
error_message.send(sender=None, text=e)
error_message(text=e)
# a version of this update already exists that is
# marked as a security update, so delete this one
affected_update.delete()
Expand All @@ -84,7 +83,7 @@ def scan_for_security_updates(self):
try:
affected_update.save()
except IntegrityError as e:
error_message.send(sender=None, text=e)
error_message(text=e)
# a version of this update already exists that is
# marked as a security update, so delete this one
affected_update.delete()
Expand All @@ -93,7 +92,7 @@ def fetch_osv_dev_data(self):
osv_dev_url = f'https://api.osv.dev/v1/vulns/{self.name}'
res = get_url(osv_dev_url)
if res.status_code == 404:
error_message.send(sender=None, text=f'404 - Skipping {self.name} - {osv_dev_url}')
error_message(text=f'404 - Skipping {self.name} - {osv_dev_url}')
return
data = res.content
osv_dev_json = json.loads(data)
Expand All @@ -102,7 +101,7 @@ def fetch_osv_dev_data(self):
def parse_osv_dev_data(self, osv_dev_json):
name = osv_dev_json.get('id')
if name != self.name:
error_message.send(sender=None, text=f'Erratum name mismatch - {self.name} != {name}')
error_message(text=f'Erratum name mismatch - {self.name} != {name}')
return
related = osv_dev_json.get('related')
if related:
Expand Down Expand Up @@ -155,7 +154,7 @@ def add_cve(self, cve_id):
""" Add a CVE to an Erratum object
"""
if not cve_id.startswith('CVE') or not cve_id.split('-')[1].isdigit():
error_message.send(sender=None, text=f'Not a CVE ID: {cve_id}')
error_message(text=f'Not a CVE ID: {cve_id}')
return
self.cves.add(get_or_create_cve(cve_id))

Expand Down
2 changes: 1 addition & 1 deletion errata/sources/distros/alma.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
from operatingsystems.utils import get_or_create_osrelease
from packages.models import Package
from packages.utils import get_or_create_package, parse_package_string
from util import get_url, fetch_content, get_setting_of_type
from patchman.signals import pbar_start, pbar_update
from util import fetch_content, get_setting_of_type, get_url


def update_alma_errata(concurrent_processing=True):
Expand Down
11 changes: 7 additions & 4 deletions errata/sources/distros/arch.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
from django.db import connections

from operatingsystems.utils import get_or_create_osrelease
from patchman.signals import error_message, pbar_start, pbar_update
from packages.models import Package
from packages.utils import find_evr, get_matching_packages, get_or_create_package
from util import get_url, fetch_content
from packages.utils import (
find_evr, get_matching_packages, get_or_create_package,
)
from patchman.signals import pbar_start, pbar_update
from util import fetch_content, get_url
from util.logging import error_message


def update_arch_errata(concurrent_processing=False):
Expand Down Expand Up @@ -99,7 +102,7 @@ def process_arch_erratum(advisory, osrelease):
add_arch_erratum_references(e, advisory)
add_arch_erratum_packages(e, advisory)
except Exception as exc:
error_message.send(sender=None, text=exc)
error_message(text=exc)


def add_arch_linux_osrelease():
Expand Down
10 changes: 6 additions & 4 deletions errata/sources/distros/centos.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
# along with Patchman. If not, see <http://www.gnu.org/licenses/>

import re

from defusedxml import ElementTree

from operatingsystems.utils import get_or_create_osrelease
from packages.models import Package
from packages.utils import parse_package_string, get_or_create_package
from patchman.signals import error_message, pbar_start, pbar_update
from util import bunzip2, get_url, fetch_content, get_sha1, get_setting_of_type
from packages.utils import get_or_create_package, parse_package_string
from patchman.signals import pbar_start, pbar_update
from util import bunzip2, fetch_content, get_setting_of_type, get_sha1, get_url
from util.logging import error_message


def update_centos_errata():
Expand All @@ -34,7 +36,7 @@ def update_centos_errata():
if actual_checksum != expected_checksum:
e = 'CEFS checksum mismatch, skipping CentOS errata parsing\n'
e += f'{actual_checksum} (actual) != {expected_checksum} (expected)'
error_message.send(sender=None, text=e)
error_message(text=e)
else:
if data:
parse_centos_errata(bunzip2(data))
Expand Down
15 changes: 8 additions & 7 deletions errata/sources/distros/debian.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@
import csv
import re
from datetime import datetime
from debian.deb822 import Dsc
from io import StringIO

from debian.deb822 import Dsc
from django.db import connections

from operatingsystems.models import OSRelease
from operatingsystems.utils import get_or_create_osrelease
from packages.models import Package
from packages.utils import get_or_create_package, find_evr
from patchman.signals import error_message, pbar_start, pbar_update, warning_message
from util import get_url, fetch_content, get_setting_of_type, extract
from packages.utils import find_evr, get_or_create_package
from patchman.signals import pbar_start, pbar_update
from util import extract, fetch_content, get_setting_of_type, get_url
from util.logging import error_message, warning_message

DSCs = {}

Expand Down Expand Up @@ -217,7 +218,7 @@ def process_debian_erratum(erratum, accepted_codenames):
for package in packages:
process_debian_erratum_fixed_packages(e, package)
except Exception as exc:
error_message.send(sender=None, text=exc)
error_message(text=exc)


def parse_debian_erratum_package(line, accepted_codenames):
Expand Down Expand Up @@ -249,7 +250,7 @@ def fetch_debian_dsc_package_list(package, version):
""" Fetch the package list from a DSC file for a given source package/version
"""
if not DSCs.get(package) or not DSCs[package].get(version):
warning_message.send(sender=None, text=f'No DSC found for {package} {version}')
warning_message(text=f'No DSC found for {package} {version}')
return
source_url = DSCs[package][version]['url']
res = get_url(source_url)
Expand All @@ -263,7 +264,7 @@ def get_accepted_debian_codenames():
""" Get acceptable Debian OS codenames
Can be overridden by specifying DEBIAN_CODENAMES in settings
"""
default_codenames = ['bookworm', 'bullseye']
default_codenames = ['bookworm', 'trixie']
accepted_codenames = get_setting_of_type(
setting_name='DEBIAN_CODENAMES',
setting_type=list,
Expand Down
Loading
Loading