From 60c22e321454ed2b390eb217116913b6f302bf40 Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Mon, 16 Dec 2024 11:28:54 +0100 Subject: [PATCH] package: spec file Provide a spec file for `image-builder-cli`. The spec file is based on `osbuild-composer`'s spec file except it is simplified as (some) usecases would currently cloud the intent of the specfile. We can add back RHEL conditionals when/if we start shipping and building for RHEL. Some tools from `osbuild-composer` are also included; most notably the one that generates bundled dependencies from vendored modules. Signed-off-by: Simon de Vlieger --- .gitignore | 1 + Makefile | 158 ++++++++++++++++++++++++++ image-builder-cli.spec | 97 ++++++++++++++++ tools/rpm_spec_add_provides_bundle.sh | 13 +++ tools/rpm_spec_vendor2provides | 63 ++++++++++ 5 files changed, 332 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 image-builder-cli.spec create mode 100755 tools/rpm_spec_add_provides_bundle.sh create mode 100755 tools/rpm_spec_vendor2provides diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b4e759 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +rpmbuild diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..318034d --- /dev/null +++ b/Makefile @@ -0,0 +1,158 @@ +# +# Maintenance Helpers +# +# This makefile contains targets used for development, as well as helpers to +# aid automatization of maintenance. Unless a target is documented in +# `make help`, it is not supported and is only meant to be used by developers +# to aid their daily development work. +# +# All supported targets honor the `SRCDIR` variable to find the source-tree. +# For most unsupported targets, you are expected to have the source-tree as +# your working directory. To specify a different source-tree, simply override +# the variable via `SRCDIR=` on the commandline. By default, the working +# directory is used for build output, but `BUILDDIR=` allows overriding +# it. +# + +BUILDDIR ?= . +SRCDIR ?= . + +RST2MAN ?= rst2man + +# see https://hub.docker.com/r/docker/golangci-lint/tags +# v1.55 to get golang 1.21 (1.21.3) +# v1.53 to get golang 1.20 (1.20.5) +GOLANGCI_LINT_VERSION=v1.55 +GOLANGCI_LINT_CACHE_DIR=$(HOME)/.cache/golangci-lint/$(GOLANGCI_LINT_VERSION) +GOLANGCI_COMPOSER_IMAGE=composer_golangci +# +# Automatic Variables +# +# This section contains a bunch of automatic variables used all over the place. +# They mostly try to fetch information from the repository sources to avoid +# hard-coding them in this makefile. +# +# Most of the variables here are pre-fetched so they will only ever be +# evaluated once. This, however, means they are always executed regardless of +# which target is run. +# +# VERSION: +# This evaluates the `Version` field of the specfile. Therefore, it will +# be set to the latest version number of this repository without any +# prefix (just a plain number). +# +# COMMIT: +# This evaluates to the latest git commit sha. This will not work if +# the source is not a git checkout. Hence, this variable is not +# pre-fetched but evaluated at time of use. +# + +VERSION := $(shell (cd "$(SRCDIR)" && grep "^Version:" image-builder-cli.spec | sed 's/[^[:digit:]]*\([[:digit:]]\+\).*/\1/')) +COMMIT = $(shell (cd "$(SRCDIR)" && git rev-parse HEAD)) + +# +# Generic Targets +# +# The following is a set of generic targets used across the makefile. The +# following targets are defined: +# +# help +# This target prints all supported targets. It is meant as +# documentation of targets we support and might use outside of this +# repository. +# This is also the default target. +# +# $(BUILDDIR)/ +# $(BUILDDIR)/%/ +# This target simply creates the specified directory. It is limited to +# the build-dir as a safety measure. Note that this requires you to use +# a trailing slash after the directory to not mix it up with regular +# files. Lastly, you mostly want this as order-only dependency, since +# timestamps on directories do not affect their content. +# + +.PHONY: help +help: + @echo "make [TARGETS...]" + @echo + @echo "This is the maintenance makefile of image-builder-cli. The following" + @echo "targets are available:" + @echo + @echo " help: Print this usage information." + @echo " rpm: Build the RPM" + @echo " srpm: Build the source RPM" + @echo " scratch: Quick scratch build of RPM" + @echo " clean: Remove all built binaries" + +$(BUILDDIR)/: + mkdir -p "$@" + +$(BUILDDIR)/%/: + mkdir -p "$@" + + +# +# Maintenance Targets +# +# The following targets are meant for development and repository maintenance. +# They are not supported nor is their use recommended in scripts. +# + +.PHONY: build +build: $(BUILDDIR)/bin/ + go build -o $ $(RPM_SPECFILE) + ./tools/rpm_spec_add_provides_bundle.sh $(RPM_SPECFILE) + +$(RPM_TARBALL): + mkdir -p $(CURDIR)/rpmbuild/SOURCES + git archive --prefix=image-builder-cli-$(COMMIT)/ --format=tar.gz HEAD > $(RPM_TARBALL) + +.PHONY: srpm +srpm: $(RPM_SPECFILE) $(RPM_TARBALL) + rpmbuild -bs \ + --define "_topdir $(CURDIR)/rpmbuild" \ + --define "commit $(COMMIT)" \ + --with tests \ + $(RPM_SPECFILE) + +.PHONY: rpm +rpm: $(RPM_SPECFILE) $(RPM_TARBALL) + rpmbuild -bb \ + --define "_topdir $(CURDIR)/rpmbuild" \ + --define "commit $(COMMIT)" \ + --with tests \ + $(RPM_SPECFILE) + +.PHONY: scratch +scratch: $(RPM_SPECFILE) $(RPM_TARBALL) + rpmbuild -bb \ + --define "_topdir $(CURDIR)/rpmbuild" \ + --define "commit $(COMMIT)" \ + --without tests \ + --nocheck \ + $(RPM_SPECFILE) diff --git a/image-builder-cli.spec b/image-builder-cli.spec new file mode 100644 index 0000000..24689d6 --- /dev/null +++ b/image-builder-cli.spec @@ -0,0 +1,97 @@ +# Do not build with tests by default +# Pass --with tests to rpmbuild to override +%bcond_with tests +%bcond_with relax_requires + +# The minimum required osbuild version +%global min_osbuild_version 129 + +%global goipath github.com/osbuild/image-builder-cli + +Version: 0 + +%gometa + +%global common_description %{expand: +A service for building customized OS artifacts, such as VM images and OSTree +commits, that uses osbuild under the hood. Besides building images for local +usage, it can also upload images directly to cloud. + +It is compatible with composer-cli and cockpit-composer clients. +} + +Name: image-builder-cli +Release: 1%{?dist} +Summary: An image building service based on osbuild +ExcludeArch: i686 armv7hl + +# Upstream license specification: Apache-2.0 +License: Apache-2.0 +URL: %{gourl} +Source0: %{gosource} + + +BuildRequires: %{?go_compiler:compiler(go-compiler)}%{!?go_compiler:golang} +BuildRequires: systemd +BuildRequires: krb5-devel +BuildRequires: python3-docutils +BuildRequires: make +# Build requirements of 'theproglottis/gpgme' package +BuildRequires: gpgme-devel +BuildRequires: libassuan-devel +# Build requirements of 'github.com/containers/storage' package +BuildRequires: device-mapper-devel +%if 0%{?fedora} +BuildRequires: systemd-rpm-macros +BuildRequires: git +# Build requirements of 'github.com/containers/storage' package +BuildRequires: btrfs-progs-devel +# DO NOT REMOVE the BUNDLE_START and BUNDLE_END markers as they are used by 'tools/rpm_spec_add_provides_bundle.sh' to generate the Provides: bundled list +# BUNDLE_START +# BUNDLE_END +%endif + +%description +%{common_description} + +%prep +%if 0%{?rhel} +%forgeautosetup -p1 +%else +%goprep -k +%endif + +%build +export GOFLAGS="-buildmode=pie" +%if 0%{?fedora} +# Fedora disables Go modules by default, but we want to use them. +# Undefine the macro which disables it to use the default behavior. +%undefine gomodulesmode +%endif + +# btrfs-progs-devel is not available on RHEL +%if 0%{?rhel} +GOTAGS="exclude_graphdriver_btrfs" +%endif + +%gobuild ${GOTAGS:+-tags=$GOTAGS} -o %{gobuilddir}/bin/image-builder %{goipath}/cmd/image-builder + +%install +install -m 0755 -vd %{buildroot}%{_bindir} +install -m 0755 -vp %{gobuilddir}/bin/image-builder %{buildroot}%{_bindir}/ + +%check +export GOFLAGS="-buildmode=pie" +%gocheck + +%files +%license LICENSE +%doc README.md +%{_bindir}/image-builder + +%changelog +# the changelog is distribution-specific, therefore there's just one entry +# to make rpmlint happy. + +* Wed Sep 11 2019 Image Builder team - 0-1 +- On this day, this project was born. diff --git a/tools/rpm_spec_add_provides_bundle.sh b/tools/rpm_spec_add_provides_bundle.sh new file mode 100755 index 0000000..790c65b --- /dev/null +++ b/tools/rpm_spec_add_provides_bundle.sh @@ -0,0 +1,13 @@ +#!/usr/bin/bash + +SPEC_FILE=${1:-"image-builder-cli.spec"} + +# Save the list of bundled packages into a file +WORKDIR=$(mktemp -d) +BUNDLES_FILE=${WORKDIR}/bundles.txt +./tools/rpm_spec_vendor2provides vendor/modules.txt > "${BUNDLES_FILE}" + +# Remove the current bundle lines +sed -i '/^# BUNDLE_START/,/^# BUNDLE_END/{//p;d;}' "${SPEC_FILE}" +# Add the new bundle lines +sed -i "/^# BUNDLE_START/r ${BUNDLES_FILE}" "${SPEC_FILE}" diff --git a/tools/rpm_spec_vendor2provides b/tools/rpm_spec_vendor2provides new file mode 100755 index 0000000..13c9aea --- /dev/null +++ b/tools/rpm_spec_vendor2provides @@ -0,0 +1,63 @@ +#!/usr/bin/python3 -s + +# Parse modules.txt files into rpm .spec file Provides for bundled dependencies. +# Written by Fabio "decathorpe" Valentini for +# the fedora syncthing package: https://src.fedoraproject.org/rpms/syncthing +# SPDX-License-Identifier: CC0-1.0 OR Unlicense + +# Modified by @gotmax23 to be used as a dependency generator +# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-FileCopyrightText: 2022 Maxwell G +# +# Minor modifications by Achilleas Koutsou to be used in +# the rpm spec file generator for https://github.com/osbuild/osbuild-composer + +import re +import sys + + +def process(path: str): + with open(path, encoding="utf-8") as file: + contents = file.read() + + lines = contents.split("\n") + + # dependencies = filter lines for "# package version" + dependencies = list(filter(lambda line: line.startswith("# "), lines)) + + # parse vendored dependencies into (import path, version) pairs + vendored = [] + # Handle => style replace directives + replace_regex = re.compile(r"^.+( v[0-9-\.]+)? => ") + for dep in dependencies: + fields = replace_regex.sub("", dep[2:]).split(" ") + if len(fields) == 2: + ipath, version = fields + elif len(fields) == 1: + ipath = fields[0] + version = "HEAD" + else: + raise RuntimeError(f"Failed to parse dependency: {dep}") + + # check for git snapshots + if len(version) > 27: + # return only 7 digits of git commit hash + version = version[-12:-1][0:7] + else: + # strip off leading "v" + version = version.lstrip("v") + + vendored.append((ipath, version)) + + for ipath, version in vendored: + print(f"Provides: bundled(golang({ipath})) = {version}") + + +def main() -> None: + files = sys.argv[1:] + for file in files: + process(file) + + +if __name__ == "__main__": + main()