build: command to build packages inside a clean chroot

This commit is contained in:
Levente Polyak 2023-01-14 18:20:29 +01:00
parent 7954382401
commit 1d7f997215
No known key found for this signature in database
GPG Key ID: FC1B547C8D8172C8
7 changed files with 525 additions and 1 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
*~ *~
devtools-*.tar.gz* devtools-*.tar.gz*
build/ /build/

View File

@ -34,6 +34,25 @@ _pkgctl_auth_status_args=(
'(-h --help)'{-h,--help}'[Display usage]' '(-h --help)'{-h,--help}'[Display usage]'
) )
_pkgctl_build_args=(
"--arch=[Specify architectures to build for (disables auto-detection)]:arch:($_arch[*])"
"--repo=[Specify a target repository (disables auto-detection)]:repo:($_repos[*])"
'(-s --staging)'{-s,--staging}'[Build against the staging counterpart of the auto-detected repo]'
'(-t --testing)'{-t,--testing}'[Build against the testing counterpart of the auto-detected repo]'
'(-o --offload)'{-o,--offload}'[Build on a remote server and transfer artifacts afterwards]'
'(-c --clean)'{-c,--clean}'[Recreate the chroot before building]'
'(-I --install)'{-I,--install}'[Install a package into the working copy of the chroot]:target:_files -g "*.pkg.tar.*(.)"'
'--pkgver=[Set pkgver, reset pkgrel and update checksums]:pkgver:'
'--pkgrel=[Set pkgrel to a given value]:pkgrel:'
'--rebuild[Increment the pkgrel variable]'
'(-e --edit)'{-e,--edit}'[Edit the PKGBUILD before building]'
'(-r --release)'{-r,--release}'[Automatically commit, tag and release after building]'
'(-m --message=)'{-m,--message=}"[Use the given <msg> as the commit message]:message:"
'(-u --db-update)'{-u,--db-update}'[Automatically update the pacman database as last action]'
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'
)
_pkgctl_db_cmds=( _pkgctl_db_cmds=(
"pkgctl db command" "pkgctl db command"
"move[Move packages between pacman repositories]" "move[Move packages between pacman repositories]"
@ -213,6 +232,7 @@ _devtools_completions_all_packages() {
_pkgctl_cmds=( _pkgctl_cmds=(
"pkgctl command" "pkgctl command"
"auth[Authenticate with services like GitLab]" "auth[Authenticate with services like GitLab]"
"build[Build packages inside a clean chroot]"
"db[Pacman database modification for packge update, move etc]" "db[Pacman database modification for packge update, move etc]"
"diff[Compare package files using different modes]" "diff[Compare package files using different modes]"
"release[Release step to commit, tag and upload build artifacts]" "release[Release step to commit, tag and upload build artifacts]"

View File

@ -0,0 +1,80 @@
pkgctl-build(1)
===============
Name
----
pkgctl-build - Build packages inside a clean chroot
Synopsis
--------
pkgctl build [OPTIONS] [PATH...]
Description
-----------
TODO
Build Options
-------------
*--arch* 'ARCH'::
Specify architectures to build for (disables auto-detection)
*--repo* 'REPO'::
Specify a target repository (disables auto-detection)
*-s, --staging*::
Build against the staging counterpart of the auto-detected repo
*-t, --testing*::
Build against the testing counterpart of the auto-detected repo
*-o, --offload*::
Build on a remote server and transfer artifacts afterwards
*-c, --clean*::
Recreate the chroot before building
*-I, --install* 'FILE'::
Install a package into the working copy of the chroot
PKGBUILD Options
----------------
*--pkgver*='PKGVER'::
Set pkgver, reset pkgrel and update checksums
*--pkgrel*='PKGREL'::
Set pkgrel to a given value
*--rebuild*::
Increment the current pkgrel variable
*-e, --edit*::
Edit the PKGBUILD before building
Release Options
---------------
*-r, --release*::
Automatically commit, tag and release after building
*-m, --message* 'MSG'::
Use the given <msg> as the commit message
*-u, --db-update*::
Automatically update the pacman database as last action
Options
-------
*-h, --help*::
Show a help text
See Also
--------
linkman:pkgctl-release[1]
linkman:pkgctl-db-update[1]
include::include/footer.asciidoc[]

View File

@ -20,6 +20,9 @@ Subcommands
pkgctl auth:: pkgctl auth::
Authenticate with services like GitLab Authenticate with services like GitLab
pkgctl build::
Build packages inside a clean chroot
pkgctl db:: pkgctl db::
Pacman database modification for packge update, move etc Pacman database modification for packge update, move etc
@ -36,6 +39,7 @@ See Also
-------- --------
linkman:pkgctl-auth[1] linkman:pkgctl-auth[1]
linkman:pkgctl-build[1]
linkman:pkgctl-db[1] linkman:pkgctl-db[1]
linkman:pkgctl-diff[1] linkman:pkgctl-diff[1]
linkman:pkgctl-release[1] linkman:pkgctl-release[1]

387
src/lib/build/build.sh Normal file
View File

@ -0,0 +1,387 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_BUILD_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_BUILD_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/db/update.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/db/update.sh
# shellcheck source=src/lib/release.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/release.sh
# shellcheck source=src/lib/util/git.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/git.sh
# shellcheck source=src/lib/util/pacman.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/pacman.sh
# shellcheck source=src/lib/valid-repos.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-repos.sh
# shellcheck source=src/lib/valid-tags.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/valid-tags.sh
source /usr/share/makepkg/util/config.sh
source /usr/share/makepkg/util/message.sh
set -e
pkgctl_build_usage() {
local -r COMMAND=${_DEVTOOLS_COMMAND:-${BASH_SOURCE[0]##*/}}
cat <<- _EOF_
Usage: ${COMMAND} [OPTIONS] [PATH]...
Build packages inside a clean chroot
When a new pkgver is set using the appropriate PKGBUILD options the
checksums are automatically updated.
TODO
BUILD OPTIONS
--arch ARCH Specify architectures to build for (disables auto-detection)
--repo REPO Specify a target repository (disables auto-detection)
-s, --staging Build against the staging counterpart of the auto-detected repo
-t, --testing Build against the testing counterpart of the auto-detected repo
-o, --offload Build on a remote server and transfer artifacts afterwards
-c, --clean Recreate the chroot before building
-I, --install FILE Install a package into the working copy of the chroot
PKGBUILD OPTIONS
--pkgver=PKGVER Set pkgver, reset pkgrel and update checksums
--pkgrel=PKGREL Set pkgrel to a given value
--rebuild Increment the current pkgrel variable
-e, --edit Edit the PKGBUILD before building
RELEASE OPTIONS
-r, --release Automatically commit, tag and release after building
-m, --message MSG Use the given <msg> as the commit message
-u, --db-update Automatically update the pacman database as last action
OPTIONS
-h, --help Show this help text
EXAMPLES
$ ${COMMAND}
$ ${COMMAND} --rebuild --staging --message 'libyay 0.42 rebuild' libfoo libbar
$ ${COMMAND} --pkgver 1.42 --release --db-update
_EOF_
}
pkgctl_build_check_option_group_repo() {
local option=$1
local repo=$2
local testing=$3
local staging=$4
if ( (( testing )) && (( staging )) ) ||
( [[ $repo =~ ^.*-(staging|testing)$ ]] && ( (( testing )) || (( staging )) )); then
die "The argument '%s' cannot be used with one or more of the other specified arguments" "${option}"
exit 1
fi
return 0
}
pkgctl_build_check_option_group_ver() {
local option=$1
local pkgver=$2
local pkgrel=$3
local rebuild=$4
if [[ -n "${pkgver}" ]] || [[ -n "${pkgrel}" ]] || (( rebuild )); then
die "The argument '%s' cannot be used with one or more of the other specified arguments" "${option}"
exit 1
fi
return 0
}
# TODO: import pgp keys
pkgctl_build() {
if (( $# < 1 )) && [[ ! -f PKGBUILD ]]; then
pkgctl_build_usage
exit 1
fi
local UPDPKGSUMS=0
local EDIT=0
local REBUILD=0
local OFFLOAD=0
local STAGING=0
local TESTING=0
local RELEASE=0
local DB_UPDATE=0
local REPO=
local PKGVER=
local PKGREL=
local MESSAGE=
local paths=()
local BUILD_ARCH=()
local BUILD_OPTIONS=()
local MAKECHROOT_OPTIONS=()
local RELEASE_OPTIONS=()
local PTS
PTS="$(tty | sed 's|/dev/pts/||')"
local WORKER="${USER}-${PTS}"
# variables
local path pkgbase pkgrepo source
while (( $# )); do
case $1 in
-h|--help)
pkgctl_build_usage
exit 0
;;
--repo)
(( $# <= 1 )) && die "missing argument for %s" "$1"
REPO="${2}"
pkgctl_build_check_option_group_repo '--repo' "${REPO}" "${TESTING}" "${STAGING}"
shift 2
;;
--arch)
(( $# <= 1 )) && die "missing argument for %s" "$1"
if [[ ${2} == all ]]; then
BUILD_ARCH=("${_arch[@]::${#_arch[@]}-1}")
elif [[ ${2} == any ]]; then
BUILD_ARCH=("${_arch[0]}")
elif ! in_array "${2}" "${BUILD_ARCH[@]}"; then
if ! in_array "${2}" "${_arch[@]}"; then
die 'invalid architecture: %s' "${2}"
fi
BUILD_ARCH+=("${2}")
fi
shift 2
;;
--pkgver=*)
pkgctl_build_check_option_group_ver '--pkgver' "${PKGVER}" "${PKGREL}" "${REBUILD}"
PKGVER="${1#*=}"
PKGREL=1
UPDPKGSUMS=1
shift
;;
--pkgrel=*)
pkgctl_build_check_option_group_ver '--pkgrel' "${PKGVER}" "${PKGREL}" "${REBUILD}"
PKGREL="${1#*=}"
shift
;;
--rebuild)
# shellcheck source=src/lib/util/git.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/util/git.sh
pkgctl_build_check_option_group_ver '--rebuild' "${PKGVER}" "${PKGREL}" "${REBUILD}"
REBUILD=1
shift
;;
-e|--edit)
EDIT=1
shift
;;
-o|--offload)
OFFLOAD=1
shift
;;
-s|--staging)
STAGING=1
pkgctl_build_check_option_group_repo '--staging' "${REPO}" "${TESTING}" "${STAGING}"
shift
;;
-t|--testing)
TESTING=1
pkgctl_build_check_option_group_repo '--testing' "${REPO}" "${TESTING}" "${STAGING}"
shift
;;
-c|--clean)
BUILD_OPTIONS+=("-c")
shift
;;
-I|--install)
(( $# <= 1 )) && die "missing argument for %s" "$1"
MAKECHROOT_OPTIONS+=("-I" "$2")
warning 'installing packages into the chroot may break reproducible builds, use with caution!'
shift 2
;;
-r|--release)
# shellcheck source=src/lib/release.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/release.sh
RELEASE=1
shift
;;
-m|--message)
(( $# <= 1 )) && die "missing argument for %s" "$1"
MESSAGE=$2
RELEASE_OPTIONS+=("--message" "${MESSAGE}")
shift 2
;;
-u|--db-update)
DB_UPDATE=1
shift
;;
--)
shift
break
;;
-*)
die "invalid argument: %s" "$1"
;;
*)
paths=("$@")
break
;;
esac
done
# check if invoked without any path from within a packaging repo
if (( ${#paths[@]} == 0 )); then
if [[ -f PKGBUILD ]]; then
paths=(".")
else
pkgctl_build_usage
exit 1
fi
fi
# Update pacman cache for auto-detection
if [[ -z ${REPO} ]]; then
update_pacman_repo_cache
# Check valid repos if not resolved dynamically
elif ! in_array "${REPO}" "${_repos[@]}"; then
die "Invalid repository target: %s" "${REPO}"
fi
for path in "${paths[@]}"; do
pushd "${path}" >/dev/null
if [[ ! -f PKGBUILD ]]; then
die 'PKGBUILD not found in %s' "${path}"
fi
source=()
# shellcheck source=contrib/makepkg/PKGBUILD.proto
. ./PKGBUILD
pkgbase=${pkgbase:-$pkgname}
pkgrepo=${REPO}
msg "Building ${pkgbase}"
# auto-detection of build target
if [[ -z ${pkgrepo} ]]; then
if ! pkgrepo=$(get_pacman_repo_from_pkgbuild PKGBUILD); then
die 'failed to get pacman repo'
fi
if [[ -z "${pkgrepo}" ]]; then
die 'unknown repo, please specify --repo for new packages'
fi
fi
# special cases to resolve final build target
if (( TESTING )); then
pkgrepo="${pkgrepo}-testing"
elif (( STAGING )); then
pkgrepo="${pkgrepo}-staging"
elif [[ $pkgrepo == core ]]; then
pkgrepo="${pkgrepo}-testing"
fi
# auto-detection of build architecture
if [[ $pkgrepo = multilib* ]]; then
BUILD_ARCH=("")
elif (( ${#BUILD_ARCH[@]} == 0 )); then
if in_array any "${arch[@]}"; then
BUILD_ARCH=("${_arch[0]}")
else
BUILD_ARCH+=("${arch[@]}")
fi
fi
# print gathered build modes
msg2 "repo: ${pkgrepo}"
msg2 "arch: ${BUILD_ARCH[*]}"
# increment pkgrel on rebuild
if (( REBUILD )); then
# try to figure out of pkgrel has been changed
if ! old_pkgrel=$(git_diff_tree HEAD PKGBUILD | grep --perl-regexp --only-matching --max-count=1 '^-pkgrel=\K\w+'); then
old_pkgrel=${pkgrel}
fi
# check if pkgrel conforms expectations
[[ ${pkgrel/.*} =~ ^[0-9]+$ ]] || die "Non-standard pkgrel declaration"
[[ ${old_pkgrel/.*} =~ ^[0-9]+$ ]] || die "Non-standard pkgrel declaration"
# increment pkgrel if it hasn't been changed yet
if [[ ${pkgrel} = "${old_pkgrel}" ]]; then
PKGREL=$((${pkgrel/.*}+1))
else
warning 'ignoring --rebuild as pkgrel has already been incremented from %s to %s' "${old_pkgrel}" "${pkgrel}"
fi
fi
# update pkgver
if [[ -n ${PKGVER} ]]; then
if [[ $(type -t pkgver) == function ]]; then
# TODO: check if die or warn, if we provide _commit _gitcommit setter maybe?
warning 'setting pkgver variable has no effect if the PKGBUILD has a pkgver() function'
fi
msg "Bumping pkgver to ${PKGVER}"
grep --extended-regexp --quiet --max-count=1 "^pkgver=${pkgver}$" PKGBUILD || die "Non-standard pkgver declaration"
sed --regexp-extended "s|^(pkgver=)${pkgver}$|\1${PKGVER}|g" -i PKGBUILD
fi
# update pkgrel
if [[ -n ${PKGREL} ]]; then
msg "Bumping pkgrel to ${PKGREL}"
grep --extended-regexp --quiet --max-count=1 "^pkgrel=${pkgrel}$" PKGBUILD || die "Non-standard pkgrel declaration"
sed --regexp-extended "s|^(pkgrel=)${pkgrel}$|\1${PKGREL}|g" -i PKGBUILD
fi
# edit PKGBUILD
if (( EDIT )); then
stat_busy 'Editing PKGBUILD'
if [[ -n $VISUAL ]]; then
$VISUAL PKGBUILD || die
elif [[ -n $EDITOR ]]; then
$EDITOR PKGBUILD || die
elif command -v vi &>/dev/null; then
vi PKGBUILD || die
else
die "need \$VISUAL or \$EDITOR to edit the PKGBUILD"
fi
stat_done
fi
# update checksums if any sources are declared
if (( UPDPKGSUMS )) && (( ${#source[@]} >= 1 )); then
updpkgsums
fi
# execute build
for arch in "${BUILD_ARCH[@]}"; do
if [[ -n $arch ]]; then
msg "Building ${pkgbase} for [${pkgrepo}] (${arch})"
BUILDTOOL="${pkgrepo}-${arch}-build"
else
msg "Building ${pkgbase} for [${pkgrepo}]"
BUILDTOOL="${pkgrepo}-build"
fi
if (( OFFLOAD )); then
offload-build --repo "${pkgrepo}" -- "${BUILD_OPTIONS[@]}" -- "${MAKECHROOT_OPTIONS[@]}" -l "${WORKER}"
else
"${BUILDTOOL}" "${BUILD_OPTIONS[@]}" -- "${MAKECHROOT_OPTIONS[@]}" -l "${WORKER}"
fi
done
# release the build
if (( RELEASE )); then
pkgctl_release --repo "${pkgrepo}" "${RELEASE_OPTIONS[@]}"
fi
# reset common PKGBUILD variables
unset pkgbase pkgname arch pkgrepo source pkgver pkgrel validpgpkeys
popd >/dev/null
done
# update the binary package repo db as last action
if (( RELEASE )) && (( DB_UPDATE )); then
# shellcheck disable=2119
pkgctl_db_update
fi
}

24
src/lib/util/git.sh Normal file
View File

@ -0,0 +1,24 @@
#!/hint/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later
[[ -z ${DEVTOOLS_INCLUDE_UTIL_GIT_SH:-} ]] || return 0
DEVTOOLS_INCLUDE_UTIL_GIT_SH=1
_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-@pkgdatadir@}
git_diff_tree() {
local commit=$1
local path=$2
git \
--no-pager \
diff \
--color=never \
--color-moved=no \
--unified=0 \
--no-prefix \
--no-ext-diff \
"${commit}" \
-- "${path}"
}

View File

@ -20,6 +20,7 @@ usage() {
COMMANDS COMMANDS
auth Authenticate with services like GitLab auth Authenticate with services like GitLab
build Build packages inside a clean chroot
db Pacman database modification for packge update, move etc db Pacman database modification for packge update, move etc
diff Compare package files using different modes diff Compare package files using different modes
release Release step to commit, tag and upload build artifacts release Release step to commit, tag and upload build artifacts
@ -46,6 +47,14 @@ while (( $# )); do
usage usage
exit 0 exit 0
;; ;;
build)
_DEVTOOLS_COMMAND+=" $1"
shift
# shellcheck source=src/lib/build/build.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/build/build.sh
pkgctl_build "$@"
exit 0
;;
repo) repo)
_DEVTOOLS_COMMAND+=" $1" _DEVTOOLS_COMMAND+=" $1"
shift shift