Compare commits

..

2 Commits

Author SHA1 Message Date
Robin Candau
873452b620 Merge branch 'version_upgrade_confirm' into 'master'
feat(version upgrade): Add diff & confirmation for pkgctl version upgrade

Closes #239

See merge request archlinux/devtools!274
2025-08-07 20:37:03 +02:00
Robin Candau
697c069b11 feat(version upgrade): Add diff output & confirmation for pkgctl version upgrade
`pkgctl version upgrade` now shows a `diff` of the changes that would be made to the PKGBUILD and asks for user's confirmation to apply them.

Changes are made to a temporary fresh copy of the PKGBUILD and are only applied to the actual PKGBUILD if the user gives confirmation to do so.
This ensures that no other potential untracked changes made prior to `pkgctl version upgrade` are lost or overwritten and it makes the overall process more "atomic" as the actual PKGBUILD is only modified once the whole upgrade process is over (and the associated changes acknowledged and verified by the user) on the temporary file; preventing eventual undesired "half-baked" changes (e.g. updated `$pkgver` but no updated checksums).

The `--noconfirm` flag can be passed to avoid being prompted for a confirmation. The applied changes are still shown however.
2024-10-10 01:33:46 +02:00
11 changed files with 116 additions and 46 deletions

View File

@@ -418,6 +418,7 @@ _pkgctl_version_setup_args__url_opts() { :; }
_pkgctl_version_upgrade_args=(
--no-update-checksums
--noconfirm
-v --verbose
-h --help
)

View File

@@ -456,6 +456,7 @@ _pkgctl_version_setup_args=(
_pkgctl_version_upgrade_args=(
'--no-update-checksums[Disable computation and update of the checksums]'
'--noconfirm[Do not ask to confirm changes made to the PKGBUILD file]'
'(-v --verbose)'{-v,--verbose}'[Display results including up-to-date versions]'
'(-h --help)'{-h,--help}'[Display usage]'
'*:git_dir:_files -/'

View File

@@ -14,7 +14,7 @@ Description
Build a PKGBUILD on a remote server using makechrootpkg. Requires a remote user
that can run archbuild in a non-interactive manner, e.g. must be able to
elevate permissions using passwordless run0.
elevate permissions using passwordless sudo.
Options
-------

View File

@@ -3,7 +3,7 @@ pkgctl-auth(1)
Name
----
pkgctl-auth - Authenticate with services like GitLab.
pkgctl-auth - Authenticate with serivces like GitLab.
Synopsis
--------

View File

@@ -38,6 +38,9 @@ Options
*--no-update-checksums*::
Disable computation and update of the checksums
*--noconfirm*::
Do not ask to confirm changes made to the PKGBUILD file
*-v, --verbose*::
Display results including up-to-date versions

View File

@@ -15,11 +15,7 @@ check_root() {
local orig_argv=("$@")
(( EUID == 0 )) && return
if type -P run0 >/dev/null; then
keepenv=",$keepenv"
command="run0 ${keepenv//,/ --setenv=}"
exec ${command} -- "${orig_argv[@]}"
elif type -P sudo >/dev/null; then
if type -P sudo >/dev/null; then
exec sudo --preserve-env="${keepenv}" -- "${orig_argv[@]}"
else
exec su root -c "$(printf ' %q' "${orig_argv[@]}")"

View File

@@ -188,7 +188,6 @@ path = [
"README.md",
"keys/**",
".SRCINFO",
".gitignore",
".nvchecker.toml",
"*.install",
"*.sysusers",

View File

@@ -21,15 +21,21 @@ pkgbuild_set_pkgver() {
local new_pkgver=$1
local pkgver=${pkgver}
if [[ -n $2 ]]; then
pkgbuild_file=$2
else
pkgbuild_file="PKGBUILD"
fi
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
if ! grep --extended-regexp --quiet --max-count=1 "^pkgver=${pkgver}$" PKGBUILD; then
if ! grep --extended-regexp --quiet --max-count=1 "^pkgver=${pkgver}$" "${pkgbuild_file}"; then
die "Non-standard pkgver declaration"
fi
sed --regexp-extended "s|^(pkgver=)${pkgver}$|\1${new_pkgver}|g" --in-place PKGBUILD
sed --regexp-extended "s|^(pkgver=)${pkgver}$|\1${new_pkgver}|g" --in-place "${pkgbuild_file}"
}
# set the pkgrel variable in a PKGBUILD
@@ -38,10 +44,16 @@ pkgbuild_set_pkgrel() {
local new_pkgrel=$1
local pkgrel=${pkgrel}
if ! grep --extended-regexp --quiet --max-count=1 "^pkgrel=${pkgrel}$" PKGBUILD; then
if [[ -n $2 ]]; then
pkgbuild_file=$2
else
pkgbuild_file="PKGBUILD"
fi
if ! grep --extended-regexp --quiet --max-count=1 "^pkgrel=${pkgrel}$" "${pkgbuild_file}"; then
die "Non-standard pkgrel declaration"
fi
sed --regexp-extended "s|^(pkgrel=)${pkgrel}$|\1${new_pkgrel}|g" --in-place PKGBUILD
sed --regexp-extended "s|^(pkgrel=)${pkgrel}$|\1${new_pkgrel}|g" --in-place "${pkgbuild_file}"
}
pkgbuild_update_checksums() {
@@ -53,6 +65,14 @@ pkgbuild_update_checksums() {
builddir=$(mktemp --tmpdir="${WORKDIR}" --directory update-checksums.XXXXXX)
newbuildfile="${builddir}/PKGBUILD"
if [[ -n $2 ]]; then
pkgbuild_file=$2
cp PKGBUILD "${builddir}/PKGBUILD-tmp" && mv -f "${pkgbuild_file}" PKGBUILD && mv "${builddir}/PKGBUILD-tmp" "${pkgbuild_file}"
else
pkgbuild_file="PKGBUILD"
fi
# generate new integrity checksums
if ! newsums=$(BUILDDIR=${builddir} makepkg_generate_integrity 2>"${status_file}"); then
printf 'Failed to generate new checksums'
@@ -81,9 +101,13 @@ pkgbuild_update_checksums() {
return 1
fi
if [[ "${pkgbuild_file}" != "PKGBUILD" ]]; then
cp PKGBUILD "${builddir}/PKGBUILD-tmp" && mv -f "${pkgbuild_file}" PKGBUILD && mv "${builddir}/PKGBUILD-tmp" "${pkgbuild_file}"
fi
# overwrite the original PKGBUILD while preserving permissions
if ! cat -- "${newbuildfile}" > PKGBUILD; then
printf "Failed to write to the PKGBUILD file"
if ! cat -- "${newbuildfile}" > "${pkgbuild_file}"; then
printf "Failed to write to the ${pkgbuild_file} file"
return 1
fi

View File

@@ -37,6 +37,7 @@ pkgctl_version_upgrade_usage() {
OPTIONS
--no-update-checksums Disable computation and update of the checksums
--noconfirm Do not ask to confirm changes made to the PKGBUILD file
-v, --verbose Display results including up-to-date versions
-h, --help Show this help text
@@ -52,6 +53,8 @@ pkgctl_version_upgrade() {
local exit_code=0
local current_item=0
local update_checksums=1
local noconfirm=0
local apply_changes=0
while (( $# )); do
case $1 in
@@ -63,6 +66,10 @@ pkgctl_version_upgrade() {
update_checksums=0
shift
;;
--noconfirm)
noconfirm=1
shift
;;
-v|--verbose)
verbose=1
shift
@@ -123,7 +130,8 @@ pkgctl_version_upgrade() {
# reset common PKGBUILD variables
unset pkgbase pkgname arch source pkgver pkgrel validpgpkeys
# shellcheck source=contrib/makepkg/PKGBUILD.proto
. ./PKGBUILD
cp -f PKGBUILD PKGBUILD_version_upgrade.tmp
. ./PKGBUILD_version_upgrade.tmp
pkgbase=${pkgbase:-$pkgname}
# update the current terminal spinner status
@@ -140,6 +148,7 @@ pkgctl_version_upgrade() {
if ! result=$(get_upstream_version); then
result="${BOLD}${pkgbase}${ALL_OFF}: ${result}"
failure+=("${result}")
rm -f PKGBUILD_version_upgrade.tmp
popd >/dev/null
continue
fi
@@ -148,6 +157,7 @@ pkgctl_version_upgrade() {
if ! result=$(vercmp "${upstream_version}" "${pkgver}"); then
result="${BOLD}${pkgbase}${ALL_OFF}: failed to compare version ${upstream_version} against ${pkgver}"
failure+=("${result}")
rm -f PKGBUILD_version_upgrade.tmp
popd >/dev/null
continue
fi
@@ -155,16 +165,19 @@ pkgctl_version_upgrade() {
if (( result == 0 )); then
result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is latest"
up_to_date+=("${result}")
rm -f PKGBUILD_version_upgrade.tmp
elif (( result < 0 )); then
result="${BOLD}${pkgbase}${ALL_OFF}: current version ${PURPLE}${pkgver}${ALL_OFF} is newer than ${DARK_GREEN}${upstream_version}${ALL_OFF}"
up_to_date+=("${result}")
rm -f PKGBUILD_version_upgrade.tmp
elif (( result > 0 )); then
result="${BOLD}${pkgbase}${ALL_OFF}: upgraded from version ${PURPLE}${pkgver}${ALL_OFF} to ${DARK_GREEN}${upstream_version}${ALL_OFF}"
out_of_date+=("${result}")
out_of_date_paths+=("${path}")
# change the PKGBUILD
pkgbuild_set_pkgver "${upstream_version}"
pkgbuild_set_pkgrel 1
# make changes to the temporary PKGBUILD file
pkgbuild_set_pkgver "${upstream_version}" PKGBUILD_version_upgrade.tmp
pkgbuild_set_pkgrel 1 PKGBUILD_version_upgrade.tmp
# download sources and update the checksums
if (( update_checksums )); then
@@ -178,9 +191,10 @@ pkgctl_version_upgrade() {
"${pkgbase}" \
"updating checksums"
if ! result=$(pkgbuild_update_checksums /dev/null); then
if ! result=$(pkgbuild_update_checksums /dev/null PKGBUILD_version_upgrade.tmp); then
result="${BOLD}${pkgbase}${ALL_OFF}: failed to update checksums for version ${DARK_GREEN}${upstream_version}${ALL_OFF}"
failure+=("${result}")
cp PKGBUILD "${status_dir}/PKGBUILD-tmp" && mv -f PKGBUILD_version_upgrade.tmp PKGBUILD && mv "${status_dir}/PKGBUILD-tmp" PKGBUILD_version_upgrade.tmp
fi
fi
fi
@@ -225,6 +239,47 @@ pkgctl_version_upgrade() {
"${#failure[@]}"
fi
# upgrade out of date packages
for ood_path in "${out_of_date_paths[@]}"; do
pushd "${ood_path}" >/dev/null
# print changes
echo
diff --unified --color PKGBUILD PKGBUILD_version_upgrade.tmp || true
# if --noconfirm is passed, directly apply changes without asking for confirmation
if (( noconfirm == 1 )); then
apply_changes=1
else
apply_changes=0
fi
# ask confirmation before making changes to the actual PKGBUILD
if (( apply_changes == 0 )); then
echo
if prompt "${GREEN}${BOLD}?${ALL_OFF} Apply changes to the PKGBUILD?"; then
apply_changes=1
fi
fi
# apply changes to the actual PKGBUILD
if (( apply_changes == 1 )); then
if ! (diff --unified PKGBUILD PKGBUILD_version_upgrade.tmp || true) | git apply; then
msg_error " Fail to apply changes" 2>&1
exit_code=1
else
echo
msg_success " Changes applied"
fi
fi
# delete temporary PKGBUILD file
rm -f PKGBUILD_version_upgrade.tmp
popd >/dev/null
done
# return status based on results
return "${exit_code}"
}

View File

@@ -185,18 +185,10 @@ prepare_chroot() {
echo "$x" >>"$copydir/etc/makepkg.conf"
done
# TODO(gromit): check if this rule is sane
# TODO(gromit): this will require a full container
cat > "$copydir/etc/polkit-1/rules.d/10-systemd-nopasswd.rules" <<EOF
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.systemd1.manage-units") {
if (subject.isInGroup("wheel")) {
return polkit.Result.YES;
}
}
});
cat > "$copydir/etc/sudoers.d/builduser-pacman" <<EOF
builduser ALL = NOPASSWD: /usr/bin/pacman
EOF
chmod 440 "$copydir/etc/polkit-1/rules.d/10-systemd-nopasswd.rules"
chmod 440 "$copydir/etc/sudoers.d/builduser-pacman"
cat > "$copydir/etc/gitconfig" <<EOF
[safe]
@@ -230,14 +222,17 @@ _chrootbuild() {
# shellcheck source=/dev/null
. /etc/profile
run0 --setenv=SOURCE_DATE_EPOCH \
--setenv=BUILDTOOL \
--setenv=BUILDTOOLVER \
--via-shell --chdir='~' \
--user=builduser -- bash -c 'cd /startdir; makepkg "$@"' -bash "$@"
# Beware, there are some stupid arbitrary rules on how you can
# use "$" in arguments to commands with "sudo -i". ${foo} or
# ${1} is OK, but $foo or $1 isn't.
# https://bugzilla.sudo.ws/show_bug.cgi?id=765
sudo --preserve-env=SOURCE_DATE_EPOCH \
--preserve-env=BUILDTOOL \
--preserve-env=BUILDTOOLVER \
-iu builduser bash -c 'cd /startdir; makepkg "$@"' -bash "$@"
ret=$?
case $ret in
0)
0|14)
return 0;;
*)
return $ret;;
@@ -248,7 +243,7 @@ _chrootnamcap() {
pacman -S --needed --noconfirm namcap
for pkgfile in /startdir/PKGBUILD /pkgdest/*; do
echo "Checking ${pkgfile##*/}"
run0 --user=builduser -- namcap "$pkgfile" 2>&1 | tee "/logdest/${pkgfile##*/}-namcap.log"
sudo -u builduser namcap "$pkgfile" 2>&1 | tee "/logdest/${pkgfile##*/}-namcap.log"
done
}
@@ -257,12 +252,8 @@ download_sources() {
chown "$makepkg_user:" "$WORKDIR"
# Ensure sources are downloaded
run0 --user="$makepkg_user" \
--setenv=GNUPGHOME \
--setenv=SSH_AUTH_SOCK \
--setenv=SRCDEST="$SRCDEST" \
--setenv=BUILDDIR="$WORKDIR" \
--chdir=. -- \
sudo -u "$makepkg_user" --preserve-env=GNUPGHOME,SSH_AUTH_SOCK \
env SRCDEST="$SRCDEST" BUILDDIR="$WORKDIR" \
makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o "${verifysource_args[@]}" ||
die "Could not download sources."
}
@@ -409,7 +400,7 @@ if arch-nspawn "$copydir" \
"${nspawn_build_args[@]}" \
/chrootbuild "${makepkg_args[@]}"
then
mapfile -t pkgnames < <(run0 --user="$makepkg_user" -- bash -c 'source PKGBUILD; printf "%s\n" "${pkgname[@]}"')
mapfile -t pkgnames < <(sudo -u "$makepkg_user" bash -c 'source PKGBUILD; printf "%s\n" "${pkgname[@]}"')
move_products
else
(( ret += 1 ))
@@ -462,7 +453,7 @@ else
done
msg2 "Checking packages"
run0 --user="$makepkg_user" -- checkpkg --rmdir --warn --makepkg-config "$copydir/etc/makepkg.conf" "${remotepkgs[@]/#file:\/\//}"
sudo -u "$makepkg_user" checkpkg --rmdir --warn --makepkg-config "$copydir/etc/makepkg.conf" "${remotepkgs[@]/#file:\/\//}"
fi
true
fi

View File

@@ -192,7 +192,7 @@ for p in "$@"; do
pkgfile=${pkgfile_remote#file://}
if [[ ! -f ${pkgfile} ]]; then
msg "Downloading package '%s' into pacman's cache" "${pkgfile}"
run0 -- pacman -Swdd --noconfirm --logfile /dev/null "${p}" || exit 1
sudo pacman -Swdd --noconfirm --logfile /dev/null "${p}" || exit 1
pkgfile_remote=$(pacman -Sddp "${p}" 2>/dev/null)
pkgfile="${pkgfile_remote#file://}"
fi