Source code for univention.updater.repository

#!/usr/bin/python3
# SPDX-FileCopyrightText: 2009-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only
"""Univention Updater helper functions for managing a local repository."""

import gzip
import os
import shutil
import subprocess
import sys
from typing import IO

from univention.config_registry import ucr
from univention.lib.ucs import UCS_Version


# constants
ARCHITECTURES = {'amd64', 'all'}


[docs] class TeeFile: """ Writes the given string to several files at once. Could by used with the print statement """ def __init__(self, fds: list[IO[str]] = []) -> None: """ Register multiple file descriptors, to which the data is written. :param fds: A list of opened files. :type fds: list(File) """ self._fds = fds or [sys.stdout]
[docs] def write(self, data: str) -> None: """ Write string to all registered files. :param str data: The string to write. """ for fd in self._fds: fd.write(data) fd.flush()
[docs] def gzip_file(filename: str) -> int: """ Compress file. :param str filename: The file name of the file to compress. :returns: the process exit code. :rtype: int """ return subprocess.call(('gzip', '--keep', '--force', '--no-name', '-9', filename))
[docs] def copy_package_files(source_dir: str, dest_dir: str) -> None: """ Copy all Debian binary package files and signed updater scripts from `source_dir` to `dest_dir`. :param str source_dir: Source directory. :param str dest_dir: Destination directory. """ for filename in os.listdir(source_dir): src = os.path.join(source_dir, filename) if not os.path.isfile(src): continue if filename.endswith(('.deb', '.udeb')): try: arch = filename.rsplit('_', 1)[-1].split('.', 1)[0] # partman-btrfs_10.3.201403242318_all.udeb except (TypeError, ValueError): print("Warning: Could not determine architecture of package '%s'" % filename, file=sys.stderr) continue src_size = os.stat(src)[6] dest = os.path.join(dest_dir, arch, filename) # package already exists with correct size if os.path.isfile(dest) and os.stat(dest)[6] == src_size: continue elif filename in ('preup.sh', 'preup.sh.gpg', 'postup.sh', 'postup.sh.gpg'): dest = os.path.join(dest_dir, 'all', filename) else: continue try: shutil.copy2(src, dest) except shutil.Error as ex: print("Copying '%s' failed: %s" % (src, ex), file=sys.stderr)
[docs] def gen_indexes(base: str, version: UCS_Version) -> None: """ Re-generate Debian :file:`Packages` files from file:`dists/` file. :param str base: Base directory, which contains the per architecture sub directories. """ A = 'Architecture: ' F = 'Filename: ' print(' generating index ...', end=' ') for arch in ARCHITECTURES: if arch == 'all': continue src = os.path.join( base, 'dists', 'ucs%d%d%d' % version.mmp, 'main', 'binary-%s' % (arch,), 'Packages.gz', ) if not os.path.exists(src): continue lines = [] names = [os.path.join(base, name, 'Packages') for name in ('all', arch)] with gzip.open(src, 'rb') as f_src, open(names[0], 'w') as f_all, open(names[1], 'w') as f_arch: for raw in f_src: line = raw.decode("UTF-8") if line.startswith(A): arch = line[len(A):].strip() elif line.startswith(F): line = '%s%s/%s' % (F, version, line[len(F):].lstrip('/')) lines.append(line) if line == '\n': f = f_all if arch == 'all' else f_arch f.write(''.join(lines)) del lines[:] for name in names: gzip_file(name) print('done')
[docs] def get_repo_basedir(packages_dir: str) -> str: """ Check if a file path is a UCS package repository. :param str package_dir: A directory path. :returns: The canonicalized path without the architecture sub directory. :rtype: str """ path = os.path.normpath(packages_dir) if os.path.isfile(os.path.join(path, 'Packages')): head, tail = os.path.split(path) if tail in ARCHITECTURES: return head elif set(os.listdir(path)) & ARCHITECTURES: return path print('Error: %s does not seem to be a repository.' % packages_dir, file=sys.stderr) sys.exit(1)
[docs] def assert_local_repository(out: IO[str] = sys.stderr) -> None: """ Exit with error if the local repository is not enabled. :param file out: Override error output. Defaults to :py:obj:`sys.stderr`. """ if not ucr.is_true('local/repository', False): print('Error: The local repository is not activated. Use "univention-repository-create" to create it or set the Univention Configuration Registry variable "local/repository" to "yes" to re-enable it.', file=out) sys.exit(1)