diff --git a/dev_scripts/env.py b/dev_scripts/env.py index 38358c8..588bac3 100755 --- a/dev_scripts/env.py +++ b/dev_scripts/env.py @@ -468,7 +468,7 @@ class Env: version = dz_version() if self.distro == "fedora": install_deps = DOCKERFILE_BUILD_FEDORA_DEPS - package = f"dangerzone-{version}-1.noarch.rpm" + package = f"dangerzone-{version}-1.fc{self.version}.x86_64.rpm" package_src = git_root() / "dist" / package package_dst = build_dir / package install_cmd = "dnf install -y" diff --git a/install/linux/build-rpm.py b/install/linux/build-rpm.py index a84f891..a1ad25c 100755 --- a/install/linux/build-rpm.py +++ b/install/linux/build-rpm.py @@ -3,20 +3,105 @@ import argparse import inspect import os +import pathlib import shutil import subprocess -import sys +import tempfile -root = os.path.dirname( - os.path.dirname( - os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) - ) -) +root = pathlib.Path(__file__).parent.parent.parent with open(os.path.join(root, "share", "version.txt")) as f: version = f.read().strip() +def remove_contents(d): + """Remove all the contents of a directory.""" + for p in d.iterdir(): + if p.is_file() or p.is_symlink(): + p.unlink() + else: + shutil.rmtree(p) + + +def build(qubes=False): + """Build an RPM package in a temporary directory. + + The build process is the following: + + 1. Clean up any stale data from previous runs under ./dist. Note that this directory + is used by `poetry build` and `rpmbuild`. + 2. Create the necessary RPM project structure under ./install/linux/rpm-build, and + use symlinks to point to ./dist, so that we don't need to move files explicitly. + 3. Create a Python source distribution using `poetry build`. If we are building a + Qubes package and there is a container image under `share/`, stash it temporarily + under a different directory. + 4. Build both binary and source RPMs using rpmbuild. Optionally, pass to the SPEC + `_qubes` flag, that denotes we want to build a package for Qubes. + """ + build_dir = root / "install" / "linux" / "rpm-build" + dist_path = root / "dist" + specfile_name = "dangerzone.spec" + specfile_path = root / "install" / "linux" / specfile_name + sdist_name = f"dangerzone-{version}.tar.gz" + + print("* Deleting old dist") + if os.path.exists(dist_path): + remove_contents(dist_path) + else: + dist_path.mkdir() + + print(f"* Creating RPM project structure under {build_dir}") + for d in ["BUILD", "BUILDROOT", "RPMS", "SOURCES", "SPECS"]: + subdir = build_dir / d + subdir.mkdir(exist_ok=True) + remove_contents(subdir) + + shutil.copy2(specfile_path, build_dir / "SPECS") + rpm_dir = build_dir / "RPMS" / "x86_64" + srpm_dir = build_dir / "SRPMS" + if srpm_dir.exists(): + os.unlink(srpm_dir) + os.symlink(dist_path, rpm_dir) + os.symlink(dist_path, srpm_dir) + + print("* Creating a Python sdist") + container_tar_gz = root / "share" / "container.tar.gz" + container_tar_gz_bak = root / "container.tar.gz.bak" + stash_container = qubes and container_tar_gz.exists() + if stash_container: + container_tar_gz.rename(container_tar_gz_bak) + try: + subprocess.run(["poetry", "build", "-f", "sdist"], cwd=root, check=True) + os.rename(dist_path / sdist_name, build_dir / "SOURCES" / sdist_name) + finally: + if stash_container: + container_tar_gz_bak.rename(container_tar_gz) + + print("* Building RPM package") + cmd = [ + "rpmbuild", + "-v", + "--define", + f"_topdir {build_dir}", + "-ba", + "--nodebuginfo", + f"{build_dir}/SPECS/dangerzone.spec", + ] + + # In case of qubes, set the `%{_qubes}` SPEC variable to 1. See the dangerzone.spec + # file for more details on how that's used. + if qubes: + cmd += [ + "--define", + f"_qubes 1", + ] + subprocess.run(cmd, check=True) + + print("") + print("The following files have been created:") + print("\n".join([str(p) for p in dist_path.iterdir()])) + + def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -24,45 +109,7 @@ def main(): ) args = parser.parse_args() - build_path = os.path.join(root, "build") - dist_path = os.path.join(root, "dist") - - print("* Deleting old build and dist") - if os.path.exists(build_path): - shutil.rmtree(build_path) - if os.path.exists(dist_path): - shutil.rmtree(dist_path) - - if args.qubes: - print("> Building for a Qubes system") - os.environ["QUBES_TARGET"] = "1" - - # Server and Client package requirements are bundled together since - # we assume the server and client qubes are installed on the same - # template - platform_dependant_packages = ",".join( - [ - # Server package requirements - "python3-magic", - "libreoffice", - # Client package requirements - "tesseract", # FIXME add other languages - ] - ) - else: - platform_dependant_packages = "podman" - - print("* Building RPM package") - subprocess.run( - f"python3 setup.py bdist_rpm --requires='{platform_dependant_packages},python3-pyside2,python3-appdirs,python3-click,python3-pyxdg,python3-colorama,python3-requests,python3-markdown,python3-packaging'", - shell=True, - cwd=root, - check=True, - ) - - print("") - print("* To install run:") - print("sudo dnf install dist/dangerzone-{}-1.noarch.rpm".format(version)) + build(args.qubes) if __name__ == "__main__": diff --git a/install/linux/dangerzone.spec b/install/linux/dangerzone.spec index 69f12db..8f9429a 100644 --- a/install/linux/dangerzone.spec +++ b/install/linux/dangerzone.spec @@ -77,6 +77,7 @@ convert the documents within a secure sandbox. %prep %autosetup -p1 -n dangerzone-%{version} + # XXX: Replace the PySide6 dependency in the pyproject.toml file with PySide2, # since the former does not exist in Fedora. Once we can completely migrate to # Qt6, we should remove this. For more details, see: @@ -84,6 +85,29 @@ convert the documents within a secure sandbox. # https://github.com/freedomofpress/dangerzone/issues/211 sed -i 's/^PySide6.*$/PySide2 = "*"/' pyproject.toml +# XXX: Replace all [tool.poetry.group.*] references in pyproject.toml with +# [tool.poetry.dev-dependencies], **ONLY** for Fedora 37. +# +# Fedora 37 ships python3-poetry-core v1.0.8. This version does not understand +# the dependency groups that were added in v1.2.0 [1]. Therefore, we need to +# dumb down the pyproject.toml file a bit, so that poetry-core can parse it. +# Note that the dev dependencies are not consulted for the creation of the RPM +# file, so doing so should be safe. +# +# The following sed invocations turn the various [tool.poetry.group.*] sections +# into one large [tool.poetry.dev-dependencies] section. Then, they patch the +# minimum required poetry-core version in pyproject.toml, to one that can be +# satisfied from the Fedora 37 repos. +# +# TODO: Remove this workaround once Fedora 37 (fedora-37) is EOL. +# +# [1]: https://python-poetry.org/docs/managing-dependencies/#dependency-groups +%if 0%{?fedora} == 37 +sed -i 's/^\[tool.poetry.group.package.*$/[tool.poetry.dev-dependencies]/' pyproject.toml +sed -i '/^\[tool.poetry.group.*$/d' pyproject.toml +sed -i 's/poetry-core>=1.2.0/poetry-core>=1.0.0/' pyproject.toml +%endif + %generate_buildrequires %pyproject_buildrequires -R