#!/usr/bin/python3
# SPDX-FileCopyrightText: 2013-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only
import os
import shutil
import subprocess
import sys
import tempfile
[docs]
class BuildRuntimeError(RuntimeError):
pass
[docs]
class InstallRuntimeError(RuntimeError):
pass
[docs]
class UninstallRuntimeError(RuntimeError):
pass
[docs]
class DebianPackage:
"""Class to build simple debian packages"""
def __init__(self, name: str = 'testdeb', version: str = '1.0') -> None:
self._package_name = name
self._package_version = version
self._create_debian_base_dir()
self.__join_file: str | None = None
self.__unjoin_file: str | None = None
def _create_debian_base_dir(self) -> None:
self._package_tempdir = tempfile.mkdtemp()
self._package_path = os.path.join(self._package_tempdir, self._package_name)
self._package_debian_path = os.path.join(self._package_path, 'debian')
os.makedirs(self._package_debian_path)
self._create_changelog()
self._create_control()
self._create_rules()
self._create_install()
[docs]
def get_package_name(self) -> str:
return self._package_name
[docs]
def get_temp_dir(self) -> str:
return self._package_tempdir
[docs]
def get_binary_name(self) -> str:
deb_file = f'{self._package_name}_{self._package_version}_all.deb'
deb_package = os.path.join(self._package_tempdir, deb_file)
return deb_package
def __create_file_from_buffer(self, path: str, file_buffer: str, write_mode: str = 'w') -> None:
with open(path, write_mode) as f:
f.write(file_buffer)
[docs]
def create_join_script_from_buffer(self, joinscript_name: str, joinscript_buffer: str) -> None:
self.__join_file = os.path.join(self._package_path, joinscript_name)
self.__create_file_from_buffer(self.__join_file, joinscript_buffer)
os.chmod(self.__join_file, 0o755)
[docs]
def create_unjoin_script_from_buffer(self, unjoinscript_name: str, unjoinscript_buffer: str) -> None:
self.__unjoin_file = os.path.join(self._package_path, unjoinscript_name)
self.__create_file_from_buffer(self.__unjoin_file, unjoinscript_buffer)
os.chmod(self.__unjoin_file, 0o755)
[docs]
def create_usr_share_file_from_buffer(self, share_filename: str, schema_buffer: str, write_mode: str = 'w') -> None:
share_file = os.path.join(self._package_path, 'usr/share/%s' % self._package_name, share_filename)
dirpath = os.path.dirname(share_file)
if not os.path.exists(dirpath):
os.makedirs(dirpath)
self.__create_file_from_buffer(share_file, schema_buffer, write_mode)
[docs]
def create_debian_file_from_buffer(self, debian_filename: str, debian_buffer: str) -> None:
deb_file = os.path.join(self._package_debian_path, debian_filename)
self.__create_file_from_buffer(deb_file, debian_buffer)
[docs]
def build(self) -> None:
install = []
if os.path.exists(os.path.join(self._package_path, 'usr/share')):
install.append('usr/share/* usr/share')
if self.__join_file:
install.append('*.inst usr/lib/univention-install/')
if self.__unjoin_file:
install.append('*.uinst usr/lib/univention-uninstall/')
self.create_debian_file_from_buffer('install', '\n'.join(install))
sys.stdout.flush()
if subprocess.call(['dpkg-buildpackage', '-rfakeroot', '-b', '-us', '-uc'], cwd=self._package_path):
raise BuildRuntimeError()
[docs]
def install(self) -> None:
deb_package = self.get_binary_name()
sys.stdout.flush()
if subprocess.call(['dpkg', '-i', deb_package]):
raise InstallRuntimeError()
[docs]
def uninstall(self, purge: bool = False) -> None:
sys.stdout.flush()
if subprocess.call(['dpkg', '-r', self._package_name]):
raise UninstallRuntimeError()
if purge and subprocess.call(['dpkg', '--purge', self._package_name]):
raise UninstallRuntimeError()
[docs]
def remove(self) -> None:
shutil.rmtree(self._package_tempdir)
def _create_changelog(self) -> None:
changelog = '''%(package_name)s (%(package_version)s) unstable; urgency=low
* Test package
-- Univention GmbH <packages@univention.de> Fri, 20 Sep 2013 01:01:01 +0200
''' % {'package_name': self._package_name, 'package_version': self._package_version}
self.create_debian_file_from_buffer('changelog', changelog)
def _create_control(self) -> None:
control = '''source: %(package_name)s
Section: univention
Priority: optional
Maintainer: Univention GmbH <packages@univention.de>
Build-Depends: debhelper-compat (=13)
Standards-Version: 3.5.2
Package: %(package_name)s
Architecture: all
Depends: ${misc:Depends}
Description: UCS - Test package
It is part of Univention Corporate Server (UCS), an
integrated, directory driven solution for managing
corporate environments. For more information about UCS,
refer to: https://www.univention.de/
''' % {'package_name': self._package_name}
self.create_debian_file_from_buffer('control', control)
def _create_rules(self) -> None:
rules = '''#!/usr/bin/make -f
%:
\tdh $@
override_dh_strip_nondeterminism: ; # Bug #46002
'''
self.create_debian_file_from_buffer('rules', rules)
def _create_install(self) -> None:
install = '''
usr/share/* usr/share/
install/* usr/lib/univention-install/
uninstall/* usr/lib/univention-uninstall/
'''
self.create_debian_file_from_buffer('install', install)
if __name__ == '__main__':
deb = DebianPackage('testdeb')
share_file = '''# testdeb
...
'''
deb.create_usr_share_file_from_buffer('test', share_file)
deb.create_join_script_from_buffer('66testdeb.inst', '...')
deb.build()
subprocess.call(['dpkg', '--contents', deb.get_binary_name()])
deb.install()
deb.uninstall()
deb.remove()