From fc22726ee8868cb65366a6131bbb256ce4928159 Mon Sep 17 00:00:00 2001 From: Ben Grande Date: Wed, 12 Jun 2024 14:44:04 +0200 Subject: [PATCH] feat: build and sign RPM packages Passing files to Dom0 is always dangerous: - Passing a git repository is dangerous as it can have ignored modified files and signature verification will pass. - Passing an archive is troublesome for updates. - Passing an RPM package depends on the RPM verification to be correct, some times it is not. - Passing a RPM repository definition is less troublesome for the user, as it is a small file to verify the contents and update mechanism is via the package manager. Trust in RPM verification is still required. Many improvements were made to the build scripts: - requires-program: Single function to check if program is installed; - spec-get: Sort project names for the usage message; - spec-get: Only running commands that are necessary; - spec-get: Fix empty summary when readme has copyright header; - spec-gen: Fix grep warning of escaped symbol; - spec-build: Sign RPM and verify signature; - spec-build: Only lint the first SPEC for faster runtime; - yumrepo-gen: Generate a local yum repository with signed metadata; - qubesbuilder-gen: Generate a .qubesbuilder based on tracked projects; - release: Build, sign and push all RPMs to repository. Goal is to be able to build with qubes-builderv2 Qubes Executor. For: https://github.com/ben-grande/qusal/issues/37 --- .pre-commit-config.yaml | 6 ++ .qubesbuilder | 59 ++++++++++++++++ .qubesbuilder.template | 9 +++ rpm_spec/template/template.spec | 19 ++++-- salt/debian-xfce/README.md | 2 +- salt/fedora-xfce/README.md | 2 +- scripts/pgp-expiration.sh | 4 ++ scripts/qubesbuilder-gen.sh | 36 ++++++++++ scripts/release.sh | 14 ++++ scripts/requires-program.sh | 19 ++++++ scripts/salt-lint.sh | 4 +- scripts/spec-build.sh | 117 ++++++++++++++++++++------------ scripts/spec-gen.sh | 16 +++-- scripts/spec-get.sh | 116 ++++++++++++++++--------------- scripts/yumrepo-gen.sh | 31 +++++++++ 15 files changed, 339 insertions(+), 115 deletions(-) create mode 100644 .qubesbuilder create mode 100644 .qubesbuilder.template create mode 100755 scripts/qubesbuilder-gen.sh create mode 100755 scripts/release.sh create mode 100755 scripts/requires-program.sh create mode 100755 scripts/yumrepo-gen.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0716857..971b885 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,3 +43,9 @@ repos: args: [--staged, --msg-filename] stages: [commit-msg] description: Lint Git commits + + - id: qubesbuilder + name: qubesbuilder + entry: scripts/qubesbuilder-gen.sh test + language: script + description: Check if .qubesbuilder is up to date diff --git a/.qubesbuilder b/.qubesbuilder new file mode 100644 index 0000000..bc257ed --- /dev/null +++ b/.qubesbuilder @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. +# +# SPDX-License-Identifier: MIT +# vim: ft=yaml +--- +host: + rpm: + build: + - rpm_spec/qusal-ansible.spec + - rpm_spec/qusal-browser.spec + - rpm_spec/qusal-debian.spec + - rpm_spec/qusal-debian-minimal.spec + - rpm_spec/qusal-debian-xfce.spec + - rpm_spec/qusal-dev.spec + - rpm_spec/qusal-docker.spec + - rpm_spec/qusal-dom0.spec + - rpm_spec/qusal-dotfiles.spec + - rpm_spec/qusal-electrum.spec + - rpm_spec/qusal-element.spec + - rpm_spec/qusal-fedora.spec + - rpm_spec/qusal-fedora-minimal.spec + - rpm_spec/qusal-fedora-xfce.spec + - rpm_spec/qusal-fetcher.spec + - rpm_spec/qusal-kicksecure-minimal.spec + - rpm_spec/qusal-mail.spec + - rpm_spec/qusal-media.spec + - rpm_spec/qusal-mgmt.spec + - rpm_spec/qusal-mirage-builder.spec + - rpm_spec/qusal-opentofu.spec + - rpm_spec/qusal-qubes-builder.spec + - rpm_spec/qusal-reader.spec + - rpm_spec/qusal-remmina.spec + - rpm_spec/qusal-signal.spec + - rpm_spec/qusal-ssh.spec + - rpm_spec/qusal-sys-audio.spec + - rpm_spec/qusal-sys-bitcoin.spec + - rpm_spec/qusal-sys-cacher.spec + - rpm_spec/qusal-sys-electrs.spec + - rpm_spec/qusal-sys-electrumx.spec + - rpm_spec/qusal-sys-firewall.spec + - rpm_spec/qusal-sys-git.spec + - rpm_spec/qusal-sys-mirage-firewall.spec + - rpm_spec/qusal-sys-net.spec + - rpm_spec/qusal-sys-pgp.spec + - rpm_spec/qusal-sys-pihole.spec + - rpm_spec/qusal-sys-print.spec + - rpm_spec/qusal-sys-rsync.spec + - rpm_spec/qusal-sys-ssh.spec + - rpm_spec/qusal-sys-ssh-agent.spec + - rpm_spec/qusal-sys-syncthing.spec + - rpm_spec/qusal-sys-usb.spec + - rpm_spec/qusal-sys-wireguard.spec + - rpm_spec/qusal-terraform.spec + - rpm_spec/qusal-usb.spec + - rpm_spec/qusal-utils.spec + - rpm_spec/qusal-vault.spec + - rpm_spec/qusal-video-companion.spec + - rpm_spec/qusal-whonix-gateway.spec + - rpm_spec/qusal-whonix-workstation.spec diff --git a/.qubesbuilder.template b/.qubesbuilder.template new file mode 100644 index 0000000..f4d03c1 --- /dev/null +++ b/.qubesbuilder.template @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. +# +# SPDX-License-Identifier: MIT +# vim: ft=yaml +--- +host: + rpm: + build: + @SPEC@ diff --git a/rpm_spec/template/template.spec b/rpm_spec/template/template.spec index 8a807a9..2853495 100644 --- a/rpm_spec/template/template.spec +++ b/rpm_spec/template/template.spec @@ -6,7 +6,6 @@ %define my_name %(./scripts/spec-get.sh @PROJECT@ name) %define branch %(./scripts/spec-get.sh @PROJECT@ branch) %define project %(./scripts/spec-get.sh @PROJECT@ project) -%define release %(./scripts/spec-get.sh @PROJECT@ release) %define summary %(./scripts/spec-get.sh @PROJECT@ summary) %define group %(./scripts/spec-get.sh @PROJECT@ group) %define vendor %(./scripts/spec-get.sh @PROJECT@ vendor) @@ -17,7 +16,7 @@ Name: %{project} Version: @VERSION@ -Release: %autorelease +Release: 1%{?dist} Summary: %{summary} Group: %{group} @@ -25,7 +24,10 @@ Vendor: %{vendor} License: %{license} URL: %{url} Source0: %{project} +BuildArch: noarch +Requires: qubes-mgmt-salt +Requires: qubes-mgmt-salt-dom0 @REQUIRES@ %description @@ -37,7 +39,9 @@ Source0: %{project} %install rm -rf %{buildroot} -mkdir -p %{buildroot}%{file_roots} +mkdir -p %{buildroot}%{file_roots} %{buildroot}/usr/share/licenses/%{project} +mv -v %{project}/LICENSES/* %{buildroot}/usr/share/licenses/%{project}/ +rm -rv %{project}/LICENSES cp -rv %{project} %{buildroot}%{file_roots}/%{my_name} %check @@ -73,10 +77,11 @@ fi %files %defattr(-,root,root,-) -%license %{file_roots}/%{my_name}/LICENSES/%{license_csv} +%license /usr/share/licenses/%{project}/* +%dir %{file_roots}/%{my_name} %doc %{file_roots}/%{my_name}/README.md -%dir %{file_roots}/%{my_name}/* -%dnl %{file_roots}/%{my_name}/* +%exclude %{file_roots}/%{my_name}/README.md +%{file_roots}/%{my_name}/* %changelog -%dnl %autochangelog +@CHANGELOG@ diff --git a/salt/debian-xfce/README.md b/salt/debian-xfce/README.md index ffe23b2..0061346 100644 --- a/salt/debian-xfce/README.md +++ b/salt/debian-xfce/README.md @@ -1,4 +1,4 @@ -# debian +# debian-xfce Debian Xfce Template in Qubes OS. diff --git a/salt/fedora-xfce/README.md b/salt/fedora-xfce/README.md index 9121c98..0061146 100644 --- a/salt/fedora-xfce/README.md +++ b/salt/fedora-xfce/README.md @@ -1,4 +1,4 @@ -# fedora +# fedora-xfce Fedora Xfce Template in Qubes OS. diff --git a/scripts/pgp-expiration.sh b/scripts/pgp-expiration.sh index 893d1fe..2ef90ee 100755 --- a/scripts/pgp-expiration.sh +++ b/scripts/pgp-expiration.sh @@ -8,6 +8,10 @@ set -eu now="$(date +%s)" fail="0" +if test -z "${1-}"; then + echo "No file provided" >&2 + exit 1 +fi for key in "${@}"; do data="$(gpg --no-keyring --no-auto-check-trustdb --no-autostart \ --with-colons --show-keys "${key}")" diff --git a/scripts/qubesbuilder-gen.sh b/scripts/qubesbuilder-gen.sh new file mode 100755 index 0000000..e0bfcff --- /dev/null +++ b/scripts/qubesbuilder-gen.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +## SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. +## +## SPDX-License-Identifier: AGPL-3.0-or-later + +set -eu + +template=".qubesbuilder.template" +target=".qubesbuilder" +intended_target="${target}" +if test "${1-}" = "test"; then + tmpdir="$(mktemp -d)" + target="${tmpdir}/.qubesbuilder" + trap 'rm -rf -- "${tmpdir}"' EXIT INT HUP QUIT ABRT +fi +ignored="$(git ls-files --exclude-standard --others --ignored)" +untracked="$(git ls-files --exclude-standard --others)" +unwanted="$(echo "${ignored}" "${untracked}" | grep "^salt/" \ + | cut -d "/" -f2 | sort -u)" +group="$(./scripts/spec-get.sh dom0 group)" +projects="$(find salt/ -mindepth 1 -maxdepth 1 -type d \ + | sort -d | sed "s|^salt/\(\S\+\)| - rpm_spec/${group}-\1.spec|")" +for unwanted_project in ${unwanted}; do + projects="$(echo "${projects}" | sed "\@rpm_spec/${group}-${unwanted_project}.spec@d")" +done + +sed -e "/@SPEC@/d" "${template}" | tee "${target}" >/dev/null +echo "${projects}" | tee -a "${target}" >/dev/null +if test "${1-}" = "test"; then + if ! cmp -s "${target}" "${intended_target}"; then + echo "${0##*/}: error: File ${intended_target} is not up to date" >&2 + echo "${0##*/}: error: Please run '${0##/*}' to update the file" >&2 + exit 1 + fi +fi diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..c04008a --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +## SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. +## +## SPDX-License-Identifier: AGPL-3.0-or-later + +set -eu + +command -v git >/dev/null || { echo "Missing program: git" >&2; exit 1; } +cd "$(git rev-parse --show-toplevel)" || exit 1 + +./scripts/qubesbuilder-gen.sh +./scripts/spec-build.sh +./scripts/yumrepo-gen.sh diff --git a/scripts/requires-program.sh b/scripts/requires-program.sh new file mode 100755 index 0000000..89eef51 --- /dev/null +++ b/scripts/requires-program.sh @@ -0,0 +1,19 @@ +#!/bin/sh +## SPDX-FileCopyrightText: 2024 Benjamin Grande M. S. +## +## SPDX-License-Identifier: AGPL-3.0-or-later + +set -eu + +missing_program=0 +for pkg in "${@}"; do + if ! command -v "${pkg}" >/dev/null; then + missing_program=1 + echo "Missing program: ${pkg}" >&2 + continue + fi +done + +if test "${missing_program}" = "1"; then + exit 1 +fi diff --git a/scripts/salt-lint.sh b/scripts/salt-lint.sh index 4687d91..eb7be8f 100755 --- a/scripts/salt-lint.sh +++ b/scripts/salt-lint.sh @@ -39,8 +39,8 @@ if test -n "${1-}"; then fi case "${find_tool}" in - fd|fdfind) files="$(${find_tool} . minion.d/ --extension=conf) $(${find_tool} . salt/ --max-depth=2 --type=f --extension=sls --extension=top)";; - find) files="$(find minion.d/ -type f -name "*.conf") $(find salt/* -maxdepth 2 -type f \( -name '*.sls' -o -name '*.top' \))";; + fd|fdfind) files="$(${find_tool} . minion.d/ --extension=conf) $(${find_tool} . salt/ --max-depth=2 --type=f --extension=sls --extension=top | sort -d)";; + find) files="$(find minion.d/ -type f -name "*.conf") $(find salt/* -maxdepth 2 -type f \( -name '*.sls' -o -name '*.top' \) | sort -d)";; esac exec salt-lint ${conf} ${files} diff --git a/scripts/spec-build.sh b/scripts/spec-build.sh index 623907b..785dbd2 100755 --- a/scripts/spec-build.sh +++ b/scripts/spec-build.sh @@ -1,67 +1,96 @@ #!/bin/sh -## SPDX-FileCopyrightText: 2023 Benjamin Grande M. S. +## SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. ## ## SPDX-License-Identifier: AGPL-3.0-or-later set -eu usage(){ - printf '%s\n' "Usage: ${0##*/} PROJECT [release]" >&2 + echo "Usage: ${0##*/} PROJECT [PROJECT ...]" >&2 exit 1 } +build_rpm(){ + counter=$((counter+1)) + project="${1}" + group="$(${spec_get} "${project}" group)" + version="$(${spec_get} "${project}" version)" + license_csv="$(${spec_get} "${project}" license_csv)" + spec="rpm_spec/${group}-${project}.spec" + + "${spec_gen}" "${project}" + + ## All specs have the same format, only lint the first one. + if test "${counter}" = "1"; then + rpmlint "${spec}" + fi + + if grep -q "^BuildRequires: " "${spec}"; then + sudo dnf build-dep "${spec}" + fi + + mkdir -p \ + "${build_dir}/BUILD/${group}-${project}/LICENSES/" \ + "${build_dir}/SOURCES/${group}-${project}/LICENSES" + + cp -r "salt/${project}/"* "${build_dir}/BUILD/${group}-${project}/" + cp -r "salt/${project}/"* "${build_dir}/SOURCES/${group}-${project}/" + for license in $(echo "${license_csv}" | tr "," " "); do + license_dir="LICENSES" + if test -d "salt/${project}/LICENSES"; then + license_dir="salt/${project}/LICENSES" + fi + cp "${license_dir}/${license}.txt" "${build_dir}/BUILD/${group}-${project}/LICENSES/" + done + + ## TODO: use qubes-builderv2 with mock or qubes executor + rpmbuild -ba -- "${spec}" + if test -n "${key_id}"; then + rpm_basename="${build_dir}/RPMS/noarch/${group}-${project}-${version}-" + rpm_suffix=".noarch.rpm" + ## TODO: target only the latest release + rpmsign --key-id="${key_id}" --digest-algo=sha512 --addsign \ + -- "${rpm_basename}"*"${rpm_suffix}" /dev/null + rpmkeys --dbpath="${dbpath}" --import "${tmp_file}" + ## TODO: target only the latest relase + rpm --dbpath="${dbpath}" --checksig --verbose \ + -- "${rpm_basename}"*"${rpm_suffix}" + fi +} + case "${1-}" in - ""|-*) usage;; + -h|--?help) usage;; esac -release="" -case "${2-}" in - release) release="1";; - "") ;; - *) usage;; -esac - -command -v dnf >/dev/null || - { printf "Missing program: dnf\n" >&2; exit 1; } -command -v rpmlint >/dev/null || - { printf "Missing program: rpmlint\n" >&2; exit 1; } -## command -v rpmdev-setuptree >/dev/null || -## { printf "Missing program: rpmdev-setuptree\n" >&2; exit 1; } -command -v rpmbuild >/dev/null || - { printf "Missing program: rpmbuild\n" >&2; exit 1; } -command -v git >/dev/null || - { printf "Missing program: git\n" >&2; exit 1; } +command -v git >/dev/null || { echo "Missing program: git" >&2; exit 1; } cd "$(git rev-parse --show-toplevel)" || exit 1 - -project="${1}" -spec_gen="./scripts/spec-gen.sh" -spec_get="./scripts/spec-get.sh" -group="$(${spec_get} "${project}" group)" -spec="rpm_spec/${group}-${project}.spec" - -"${spec_gen}" "${project}" -rpmlint "${spec}" - -if grep -q "^BuildRequires: " "${spec}"; then - sudo dnf build-dep "${spec}" -fi +./scripts/requires-program.sh dnf rpmlint rpmbuild rpmsign +build_dir="${HOME}/rpmbuild" if command -v rpmdev-setuptree >/dev/null; then rpmdev-setuptree else - mkdir -p ~/rpmbuild/BUILD ~/rpmbuild/BUILDROOT ~/rpmbuild/RPMS - mkdir -p ~/rpmbuild/SOURCES ~/rpmbuild/SPECS ~/rpmbuild/SRPMS + mkdir -p \ + "${build_dir}/BUILD" "${build_dir}/BUILDROOT" "${build_dir}/RPMS" \ + "${build_dir}/SOURCES" "${build_dir}/SPECS" "${build_dir}/SRPMS" fi -mkdir ~/rpmbuild/BUILD/"${group}-${project}" -mkdir ~/rpmbuild/SOURCES/"${group}-${project}" +key_id="$(git config --get user.signingKey)" || true +spec_gen="./scripts/spec-gen.sh" +spec_get="./scripts/spec-get.sh" -cp -r "salt/${project}"/* ~/rpmbuild/BUILD/"${group}-${project}"/ -cp -r "salt/${project}"/* ~/rpmbuild/SOURCES/"${group}-${project}"/ - -if test -n "${release}"; then - rpmbuild -ba --sign "${spec}" -else - rpmbuild -ba "${spec}" +if test -z "${1-}"; then + # shellcheck disable=SC2046 + set -- $(find salt/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \ + | sort -d | tr "\n" " ") fi +counter=0 +for p in "$@"; do + build_rpm "${p}" +done diff --git a/scripts/spec-gen.sh b/scripts/spec-gen.sh index 38a7813..fef9a98 100755 --- a/scripts/spec-gen.sh +++ b/scripts/spec-gen.sh @@ -15,8 +15,8 @@ usage(){ ## Get scriptlet command, else fail safe. get_scriptlet(){ scriptlet="$1" - sed -n "/^<\!-- pkg:begin:${scriptlet} -->$/,/^<\!-- pkg:end:${scriptlet} -->$/p" "${readme}" \ - | grep -v -e '^```\S*$' -e "^<\!-- " | sed "s/^sudo //" || echo "true" + sed -n "/^<\!-- pkg:begin:${scriptlet} -->$/,/^<\!-- pkg:end:${scriptlet} -->$/p" \ + "${readme}" | sed '/^```.*/d;/^<\!-- pkg:/d;s/^sudo //' || echo "true" } get_spec(){ @@ -68,7 +68,7 @@ gen_spec(){ "${template}" | tee "${target}" >/dev/null requires_key="" - for r in $(printf %s"${requires}" | tr " " "\n"); do + for r in $(printf %s"${requires}" | tr " " "\n" | sort -u); do requires_key="${requires_key}\nRequires: ${group}-${r}" done sed -i "s/@REQUIRES@/${requires_key}/" "${target}" >/dev/null @@ -76,15 +76,19 @@ gen_spec(){ } case "${1-}" in - ""|-h|--?help) usage; exit 1;; + -h|--?help) usage; exit 1;; esac -command -v git >/dev/null || - { printf "Missing program: git\n" >&2; exit 1; } +command -v git >/dev/null || { echo "Missing program: git" >&2; exit 1; } cd "$(git rev-parse --show-toplevel)" spec_get="./scripts/spec-get.sh" +if test -z "${1-}"; then + # shellcheck disable=SC2046 + set -- $(find salt/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \ + | sort -d | tr "\n" " ") +fi for p in "$@"; do gen_spec "${p}" done diff --git a/scripts/spec-get.sh b/scripts/spec-get.sh index 4b4ea51..5fc10cd 100755 --- a/scripts/spec-get.sh +++ b/scripts/spec-get.sh @@ -8,7 +8,8 @@ set -eu usage(){ - names="$(find salt/ -maxdepth 1 -type d | cut -d "/" -f2 | tr "\n" " ")" + names="$(find salt/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \ + | sort -d | tr "\n" " ")" echo "Usage: ${0##*/} " echo "Example: ${0##*/} qubes-builder description" echo "Names: ${names}" @@ -28,29 +29,34 @@ block_max_chars(){ keys="name branch group file_roots requires vendor url version project project_dir changelog readme license_csv license description summary saltfiles" +name="" +key="" case "${1-}" in "") usage; exit 1;; -h|--?help) usage; exit 0;; + *) name="${1}"; shift;; esac -case "${2-}" in +case "${1-}" in "") usage; exit 1;; + *) key="${1}"; shift;; esac +if test -z "${key##* }"; then + echo "Key is emtpy: ${key}" >&2 + exit 1 +fi -command -v reuse >/dev/null || - { printf "Missing program: reuse\n" >&2; exit 1; } -command -v git >/dev/null || - { printf "Missing program: git\n" >&2; exit 1; } +command -v git >/dev/null || { echo "Missing program: git" >&2; exit 1; } cd "$(git rev-parse --show-toplevel)" || exit 1 +./scripts/requires-program.sh reuse + +if test "${key}" = "branch"; then + branch="$(git branch --show-current)" +fi -name="${1}" -key="${2}" -branch="$(git branch --show-current)" group="qusal" block_max_chars group "${group}" 70 - file_roots="/srv/salt/${group}" vendor="Benjamin Grande" - url="https://github.com/ben-grande/qusal" version="1.0" @@ -68,51 +74,53 @@ if ! test -f "${readme}"; then exit 1 fi -license_csv="$(reuse --root "${project_dir}" lint | - awk -F ':' '/^* Used licenses:/{print $2}' | tr -d " ")" -license="$(echo "$license_csv" | sed "s/,/ AND /g")" -#license="$(reuse --root "${project_dir}" lint | -# awk -F ':' '/^* Used licenses:/{print $2}' | sed "s|, | AND |g")" - -## The problem with %autochangelog is that it will print logs of all projects -## and we separate a project per directory. -## The disadvantage of the changelog below is that it doesn't differentiate -## commits per package release. -changelog="$(TZ=UTC0 git log -n 50 --format=format:"* %cd %an <%ae> - %h%n- %s%n%n" --date=format:"%a %b %d %Y" -- "${project_dir}" | sed -re "s/^- +- */- /;/^$/d")" - -#block_max_chars license "${license}" 70 -description="$(sed -n '/^## Description/,/^## /p' "${readme}" | - sed '1d;$d' | sed "1{/^$/d}")" -summary="$(sed -n '3p' "${readme}")" -block_max_chars summary "${summary}" 70 - -saltfiles="$(find "${project_dir}" -maxdepth 1 -name "*.sls")" -# shellcheck disable=SC2086 -if test -n "${saltfiles}"; then - requires="$(sed -n '/^include:$/,/^\s*$/p' ${saltfiles} | sed "/^\s*- \./d;/{/d" | grep "^\s*- " | cut -d "." -f1 | sort -u | sed "s/- //")" - if grep -qrn "{%-\? from \('\|\"\)utils" ${saltfiles}; then - if test -n "${requires}"; then - requires="${requires} utils" - else - requires="utils" - fi - fi -else - requires="" +if test "${key}" = "license" || test "${key}" = "license_csv"; then + license_csv="$(reuse --root "${project_dir}" lint | + awk -F ':' '/^* Used licenses:/{print $2}' | tr -d " ")" + license="$(echo "${license_csv}" | sed "s/,/ AND /g")" fi -requires_valid="" -for r in $(printf %s"${requires}" | tr " " "\n"); do - if ! test -d "salt/${r}"; then - continue - fi - requires_valid="${requires_valid} ${r}" -done -requires="${requires_valid}" -unset requires_valid -if test -z "${key}" || test "$(echo "${key}" | sed "s/ //g")" = ""; then - echo "Key has no value: ${key}" >&2 - exit 1 +## The macro %autochangelog prints logs of all projects and we separate a +## project per directory. The disadvantage of the changelog below is it +# #doesn't differentiate commits per version and release, but per commit id. +if test "${key}" = "changelog"; then + changelog="$(TZ=UTC0 git log -n 50 --format=format:"* %cd %an <%ae> - %h%n- %s%n%n" --date=format:"%a %b %d %Y" -- "${project_dir}" | sed -re "s/^- +- */- /;/^$/d")" +fi + +if test "${key}" = "description"; then + description="$(sed -n '/^## Description/,/^## /p' "${readme}" | + sed '1d;$d' | sed "1{/^$/d}")" +fi + +if test "${key}" = "summary"; then + summary="$(sed -n "/^# ${name}$/,/^## Table of Contents$/{/./!d;/^#/d;p}" "${readme}")" + block_max_chars summary "${summary}" 70 +fi + +if test "${key}" = "saltfiles" || test "${key}" = "requires"; then + saltfiles="$(find "${project_dir}" -maxdepth 1 -name "*.sls")" + # shellcheck disable=SC2086 + if test -n "${saltfiles}"; then + requires="$(sed -n '/^include:$/,/^\s*$/p' ${saltfiles} | sed "/^\s*- \./d;/{/d" | grep "^\s*- " | cut -d "." -f1 | sort -u | sed "s/- //")" + if grep -qrn "{%-\? from \('\|\"\)utils" ${saltfiles}; then + if test -n "${requires}"; then + requires="${requires} utils" + else + requires="utils" + fi + fi + else + requires="" + fi + requires_valid="" + for r in $(printf %s"${requires}" | tr " " "\n"); do + if ! test -d "salt/${r}"; then + continue + fi + requires_valid="${requires_valid} ${r}" + done + requires="${requires_valid}" + unset requires_valid fi case "${key}" in diff --git a/scripts/yumrepo-gen.sh b/scripts/yumrepo-gen.sh new file mode 100755 index 0000000..752b398 --- /dev/null +++ b/scripts/yumrepo-gen.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +## SPDX-FileCopyrightText: 2023 - 2024 Benjamin Grande M. S. +## +## SPDX-License-Identifier: AGPL-3.0-or-later + +set -eu + +command -v git >/dev/null || { echo "Missing program: git" >&2; exit 1; } +cd "$(git rev-parse --show-toplevel)" || exit 1 +./scripts/requires-program.sh createrepo_c gpg + +key_id="$(git config --get user.signingKey)" || true +build_dir="${HOME}/rpmbuild" +qubes_release="r4.2" +repo="current" +dist="fc37" +yum_repo_root="$HOME/rpmrepo" +yum_repo="${yum_repo_root}/${qubes_release}/${repo}/host/${dist}" + +mkdir -p "${yum_repo}/rpm" +find "${build_dir}/RPMS/" -type f -name "*.rpm" \ + -exec cp {} "${yum_repo}/rpm/" \; +createrepo_c --checksum sha512 "${yum_repo}" +if test -n "${key_id}"; then + rm -f -- "${yum_repo}/repodata/repomd.xml.asc" + gpg --batch --no-tty --detach-sign --armor --local-user "${key_id}" \ + -- "${yum_repo}/repodata/repomd.xml" +fi + +## TODO: rsync to remote host