summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLevente Polyak <anthraxx@archlinux.org>2023-01-14 18:20:29 +0100
committerLevente Polyak <anthraxx@archlinux.org>2023-05-20 00:08:11 +0200
commit1d7f9972151b0a52297880c8f9e5f28a1d7fe597 (patch)
treec33829e2d30756d219ea470b07abbabd76f899d0
parent79543824011ba51ddad6a4dda8c8c585ea8e5e0a (diff)
downloaddevtools-1d7f9972151b0a52297880c8f9e5f28a1d7fe597.tar.xz
build: command to build packages inside a clean chroot
-rw-r--r--.gitignore2
-rw-r--r--contrib/completion/zsh/_devtools.in20
-rw-r--r--doc/man/pkgctl-build.1.asciidoc80
-rw-r--r--doc/man/pkgctl.1.asciidoc4
-rw-r--r--src/lib/build/build.sh387
-rw-r--r--src/lib/util/git.sh24
-rw-r--r--src/pkgctl.in9
7 files changed, 525 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index f5d2d27..3e34a0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
*~
devtools-*.tar.gz*
-build/
+/build/
diff --git a/contrib/completion/zsh/_devtools.in b/contrib/completion/zsh/_devtools.in
index 2961f6f..c743667 100644
--- a/contrib/completion/zsh/_devtools.in
+++ b/contrib/completion/zsh/_devtools.in
@@ -34,6 +34,25 @@ _pkgctl_auth_status_args=(
'(-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 command"
"move[Move packages between pacman repositories]"
@@ -213,6 +232,7 @@ _devtools_completions_all_packages() {
_pkgctl_cmds=(
"pkgctl command"
"auth[Authenticate with services like GitLab]"
+ "build[Build packages inside a clean chroot]"
"db[Pacman database modification for packge update, move etc]"
"diff[Compare package files using different modes]"
"release[Release step to commit, tag and upload build artifacts]"
diff --git a/doc/man/pkgctl-build.1.asciidoc b/doc/man/pkgctl-build.1.asciidoc
new file mode 100644
index 0000000..132c0a4
--- /dev/null
+++ b/doc/man/pkgctl-build.1.asciidoc
@@ -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[]
diff --git a/doc/man/pkgctl.1.asciidoc b/doc/man/pkgctl.1.asciidoc
index 8da5d19..c5a6174 100644
--- a/doc/man/pkgctl.1.asciidoc
+++ b/doc/man/pkgctl.1.asciidoc
@@ -20,6 +20,9 @@ Subcommands
pkgctl auth::
Authenticate with services like GitLab
+pkgctl build::
+ Build packages inside a clean chroot
+
pkgctl db::
Pacman database modification for packge update, move etc
@@ -36,6 +39,7 @@ See Also
--------
linkman:pkgctl-auth[1]
+linkman:pkgctl-build[1]
linkman:pkgctl-db[1]
linkman:pkgctl-diff[1]
linkman:pkgctl-release[1]
diff --git a/src/lib/build/build.sh b/src/lib/build/build.sh
new file mode 100644
index 0000000..e32c79f
--- /dev/null
+++ b/src/lib/build/build.sh
@@ -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
+}
diff --git a/src/lib/util/git.sh b/src/lib/util/git.sh
new file mode 100644
index 0000000..c4af662
--- /dev/null
+++ b/src/lib/util/git.sh
@@ -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}"
+}
diff --git a/src/pkgctl.in b/src/pkgctl.in
index 47409de..40f9259 100644
--- a/src/pkgctl.in
+++ b/src/pkgctl.in
@@ -20,6 +20,7 @@ usage() {
COMMANDS
auth Authenticate with services like GitLab
+ build Build packages inside a clean chroot
db Pacman database modification for packge update, move etc
diff Compare package files using different modes
release Release step to commit, tag and upload build artifacts
@@ -46,6 +47,14 @@ while (( $# )); do
usage
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)
_DEVTOOLS_COMMAND+=" $1"
shift