Compare commits

...
20 Commits
Author SHA1 Message Date
wyj 96e0ccfbdd fix: clean the message file
Fixes #3
2026-02-06 16:44:42 -05:00
wyj 4b895a0591 fix: clean the home and temdir 2026-02-06 16:28:33 -05:00
wyj 94379cb720 feat: set terminal title 2026-02-06 15:46:51 -05:00
wyj 7587d72578 update: fix PATH 2026-01-19 17:31:33 -05:00
wyj cfe99f928a update: change filedir 2026-01-19 17:30:10 -05:00
wyj 2fc05ba7ea update: safe leaf_update_package_datebase 2026-01-17 16:18:42 -05:00
wyj 30fed4b734 update: add leaf query 2026-01-17 15:17:42 -05:00
wyj caf66028e6 update: add leaf_info to print 2026-01-14 00:27:18 -05:00
wyj d5188a3385 update: update readme 2026-01-11 05:10:21 -05:00
wyj 618ad9222a feat: allow sha512sums ans sha256sums 2026-01-11 05:08:00 -05:00
wyj 4ff79cfdd6 update: seperate HOME and TEMPDIR
now setup LEAFHOME and LEAFTEMPDIR to /tmp/leaf-tmp/$PKG_NAME
and export HOME=$LEAFHOME TEMPDIR=$LEAFTEMPDIR
in this way, HOME and TEMPDIR is out of source tree, which is better
also, path is shorter
2026-01-02 01:30:13 -05:00
wyj 1386633baf update: add remove output 2025-12-29 17:49:34 -05:00
wyj 24c9bc3758 feat: add leaf upgrade old new for upgrading 2025-12-29 17:40:55 -05:00
wyj d0600c4b07 feat: add CONFFILES to record /etc files 2025-12-29 01:40:45 -05:00
wyj db903ca802 fix: fix a bug in leaf_remove_package
the _directory varibale lost $
2025-12-28 13:38:40 -05:00
wyj 57ea39b406 feat: check dummy function, do not echo 2025-12-26 23:32:34 -05:00
wyj f2cab01e36 fix: update envs 2025-12-26 19:05:20 -05:00
wyj a11405d93f fix: fix message by write to a temp message file
fixes #1
2025-12-26 18:02:02 -05:00
wyj c682ef1049 fix: errexit fix 2025-12-26 17:10:38 -05:00
wyj d44c761978 update: use filedir 2025-12-26 14:32:11 -05:00
2 changed files with 583 additions and 92 deletions
+3
View File
@@ -65,6 +65,9 @@
- license 数组, 软件许可证
- sources 数组, 构建过程使用到的所有源码包和补丁,可以是 `https://...` 也可以是文件相对路径
- urls 数组, `sources` 各项对应的地址, 当前仅限使用 `wget` 获取或复制文件
- 以下三个数组有一个即可
- sha512sums 数组, `sources` 各项对应的 sha512 checksum
- sha256sums 数组, `sources` 各项对应的 sha256 checksum
- md5sums 数组, `sources` 各项对应的 md5 checksum
- options 数组, 构建选项, 详见第4小节
- srcdir 字符串, 构建路径(在leaf主程序中定义)
+570 -82
View File
@@ -1,14 +1,16 @@
#!/bin/bash
set -e
shopt -s inherit_errexit 2>/dev/null || true
RED_COLOR="\e[1;31m"
PURPLE_COLOR="\e[1;35m"
GREEN_COLOR="\e[1;32m"
CLEAR_COLOR="\e[0m"
RED_COLOR=$"\e[1;31m"
PURPLE_COLOR=$"\e[1;35m"
GREEN_COLOR=$'\e[1;32m'
CLEAR_COLOR=$'\e[0m'
_DEF_PARALLEL_JOBS=4
_ENV_PARALLEL_JOBS="${PARALLEL_JOBS}"
source /etc/leaf.conf
export PATH="/usr/bin:/usr/sbin"
if [ -n "${_ENV_PARALLEL_JOBS}" ]; then
export PARALLEL_JOBS="${_ENV_PARALLEL_JOBS}"
elif [ -z "${PARALLEL_JOBS}" ]; then
@@ -18,8 +20,11 @@ else
fi
export MAKEFLAGS="-j${PARALLEL_JOBS}"
export NINJAJOBS="${PARALLEL_JOBS}"
export LC_ALL=C
export TESTSUITEFLAGS="-j${PARALLEL_JOBS}"
export LANG=C
export LC_CTYPE=C.UTF-8
export leaf_flags="CFLAGS CXXFLAGS FCFLAGS FFLAGS RUSTFLAGS"
export LEAF_MESSAGE_DIR="${TEMP_DIR:-/tmp}/leaf-messages"
for flag in ${leaf_flags}; do
export $flag
done
@@ -27,7 +32,15 @@ done
declare -A BUILD_OPTIONS
BUILD_OPTIONS=([strip]="0" [libtool]="0" [zipman]="0")
# global message file list
declare -a LEAF_MESSAGE_FILES=()
leaf_set_title() {
printf '\e]2;%s\a' "$*"
}
main() {
leaf_set_title "leaf"
leaf_check_directories
if [ $# -le 1 ]; then
case $1 in
@@ -54,6 +67,7 @@ main() {
leaf_find_pkgbuild "${_item}"
fi
leaf_reset_state
leaf_message_init
if [[ x"$1" == x"remove" ]]; then
source "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/PKGBUILD" || leaf_record_message "cannot find PKGBUILD, ingore."
else
@@ -61,13 +75,24 @@ main() {
fi
leaf_parse_options
srcdir="${BUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
LEAFTMPDIR="/tmp/leaf-tmp/${PKG_NAME}"
LEAFHOME="/tmp/leaf-tmp/${PKG_NAME}"
export TMPDIR="${LEAFTMPDIR}"
export HOME=${LEAFHOME}
pkgdir="${srcdir}"/__pkgdir__
if [[ x"$1" != x"dirct-install" ]]; then
rm -rf "${srcdir}" && install -dm755 "${srcdir}" && cd "${srcdir}"
fi
leaf_${1}_package
#rm -rf "${LEAFTMPDIR}"
#rm -rf "${LEAFHOME}"
done
;;
upgrade)
leaf_check_permission
[ $# -eq 3 ] || leaf_error "Usage: leaf upgrade <oldpkg> <newpkg>"
leaf_upgrade_package "$2" "$3"
;;
search)
leaf_search_package "${@:2}"
;;
@@ -84,13 +109,16 @@ main() {
leaf_unpack_package "${_item}"
done
;;
query)
shift
[[ $# -eq 1 ]] || leaf_error "usage: leaf query <atom-or-name>"
leaf_query "$1"
;;
*)
leaf_print_usage
;;
esac
if [ -n "${MESSAGE}" ]; then
echo -e "${MESSAGE}"
fi
leaf_print_message
fi
}
@@ -98,6 +126,10 @@ main() {
# Help functions that give PKGBUILD to use
###########################################################
leaf_info() {
echo -e "${GREEN_COLOR} * $1${CLEAR_COLOR}"
}
leaf_clear_flags() {
for flag in $leaf_flags; do
unset $flag
@@ -167,9 +199,7 @@ leaf_compress_man_pages() {
}
leaf_error() {
if [ -n "${MESSAGE}" ]; then
echo -e "${MESSAGE}"
fi
leaf_print_message
echo -e "${RED_COLOR}* $1${CLEAR_COLOR}" && exit 1
}
@@ -228,6 +258,7 @@ leaf_invoke_hooks() {
leaf_merge_package() {
pushd $1 > /dev/null 2>&1
leaf_set_title "leaf: ${PKG_PREFIX}/${PKG_NAME} (Merging)"
local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
local _item _item_conflict _mode _owner _group
local _time="$(date +%Y%m%d%H%M%S)"
@@ -237,27 +268,46 @@ leaf_merge_package() {
_group=$(stat -c %g ."${_item}")
install -d -m ${_mode} -o ${_owner} -g ${_group} "${_item}"
done
cat "${_trace_dir}"/FILES | while read -r _item; do
_mode=$(stat -c %a ."${_item}")
_owner=$(stat -c %u ."${_item}")
_group=$(stat -c %g ."${_item}")
if [[ "${_item}" == /etc/* && -e "${_item}" ]]; then
# Fast path: non-/etc, or /etc path that doesn't exist yet -> install/overwrite directly
if [[ "${_item}" != /etc/* || ! -e "${_item}" ]]; then
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
continue
fi
# Now: /etc/* and already exists on the system.
# Upgrade optimization: if OLD_TRACE_DIR is set and the current /etc file matches old baseline,
# then user didn't modify it -> overwrite with new version directly.
if [[ -n "${OLD_TRACE_DIR:-}" ]] \
&& leaf_conffile_is_unmodified "${_item}" "${OLD_TRACE_DIR}"; then
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
continue
fi
# Otherwise, fall back to conflict logic:
# - if content is identical -> overwrite (keeps permissions/ownership from pkg)
# - else -> write conflict file and keep user's current file
_item_conflict="$(dirname "${_item}")/._$(basename "${_item}").conflict_${PKG_NAME}_${_time}"
if (diff ."${_item}" "${_item}" > /dev/null); then
if diff -q ."${_item}" "${_item}" >/dev/null 2>&1; then
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
else
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item_conflict}"
leaf_record_message "Config file confliction on ${_item}, the package provided version is installed as ${_item_conflict}."
fi
else
install -D -m ${_mode} -o ${_owner} -g ${_group} ."${_item}" "${_item}"
fi
done
cat "${_trace_dir}"/LINKS | while read -r _item; do
cp -dp ."${_item}" "${_item}"
done
leaf_invoke_hooks install
popd > /dev/null 2>&1
rm -rf "${LEAFHOME}"
rm -rf "${LEAFTMPDIR}"
}
leaf_parse_options() {
@@ -290,9 +340,10 @@ Usage: leaf [option] [packages]
Option: prepare prepare packages but do not build
build build packages but do not install
install install packages
forece-install install packages even if test faild
forece-install install packages even if test failed
dirct-install install packages directly from previous build
remove remove packages
upgrade upgrade oldpkg to newpkg (leaf upgrade old new)
clean clean all source folders
list list installed packages
search search packages by keyword
@@ -303,41 +354,90 @@ Option: prepare prepare packages but do not build
EOF
}
leaf_message_init() {
# one log per package invocation
local d="${LEAF_MESSAGE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
install -dm755 -- "$d"
LEAF_MESSAGE_FILE="${d}/messages.$$.$(date +%Y%m%d%H%M%S).log"
: > "${LEAF_MESSAGE_FILE}"
export LEAF_MESSAGE_FILE
LEAF_MESSAGE_FILES+=("${LEAF_MESSAGE_FILE}")
}
leaf_record_message() {
MESSAGE+="\n${PURPLE_COLOR}* Message from ${PKG_PREFIX}/${PKG_NAME}${CLEAR_COLOR}\n"
local out
local _item
for _item in "${@:1}"; do
MESSAGE+="${GREEN_COLOR}* ${_item}${CLEAR_COLOR}\n"
out+="${GREEN_COLOR}* ${_item}${CLEAR_COLOR}\n"
done
if [ -n "${LEAF_MESSAGE_FILE}" ]; then
# printf %b will explain \n 和 \e
printf '%b' "${out}" >> "${LEAF_MESSAGE_FILE}"
fi
}
leaf_print_message() {
local f prefix name
for f in "${LEAF_MESSAGE_FILES[@]}"; do
[ -n "$f" ] || continue
[ -s "$f" ] || continue
# ${TEMP_DIR}/leaf-messages/<prefix>/<name>/messages....
prefix="${f#${TEMP_DIR}/leaf-messages/}"
prefix="${prefix%%/*}"
name="${f#${TEMP_DIR}/leaf-messages/${prefix}/}"
name="${name%%/*}"
echo -e "\n${PURPLE_COLOR}* Message from ${prefix}/${name}${CLEAR_COLOR}\n"
cat -- "$f"
done
rm -rf -- "${LEAF_MESSAGE_DIR}"/*
LEAF_MESSAGE_FILES=()
unset LEAF_MESSAGE_FILE
}
leaf_remove_libtool_files() {
find "${pkgdir}" -name "*.la" -delete
}
leaf_noop() { :; }
leaf_reset_state() {
options=()
src_preinstall() {
:
}
src_postinstall() {
:
}
src_preremove() {
:
}
src_postremove() {
:
}
src_prepare() {
:
}
src_build() {
:
}
src_check() {
:
src_preinstall() { leaf_noop; }
src_postinstall() { leaf_noop; }
src_preremove() { leaf_noop; }
src_postremove() { leaf_noop; }
src_prepare() { leaf_noop; }
src_build() { leaf_noop; }
src_check() { leaf_noop; }
src_install() { leaf_noop; }
}
leaf_is_noop_func() {
# usage: leaf_is_noop_func <funcname>
local fn="$1" body
declare -F "$fn" >/dev/null || return 0
body="$(declare -f "$fn" \
| sed -n '0,/{/d; :a; /}/q; p; n; ba')"
body="$(printf '%s' "$body" | tr -d '[:space:]' | tr -d ';')"
# allowd no-op form: leaf_noop, :, true
case "$body" in
leaf_noop|:|true) return 0 ;;
esac
return 1
}
leaf_strip_files() {
@@ -383,35 +483,187 @@ leaf_trace_package() {
find . -type l | sort > "${_trace_dir}"/LINKS
sed -i "s/.//" "${_trace_dir}"/{DIRS,FILES,LINKS}
install -vm644 "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD" "${_trace_dir}/PKGBUILD"
leaf_trace_conffiles "${_trace_dir}"
popd > /dev/null 2>&1
}
leaf_update_package_database() {
local _item="$(echo "$2" | awk 'BEGIN{FS="/";OFS="\\/"}{print $1,$2}')"
case $1 in
add)
sed -i "/${_item}/d" "${INSTALLED_PACKAGES}"
echo "$2" >> ${INSTALLED_PACKAGES}
leaf_trace_conffiles() {
# usage: leaf_trace_conffiles <trace_dir>
# called with CWD = pkgdir (same as leaf_trace_package)
local _trace_dir="$1"
local _out="${_trace_dir}/CONFFILES"
local _p _type _val _mode _uid _gid
[ -d ./etc ] || return 0
: > "${_out}"
# regular files
while IFS= read -r -d '' _p; do
_mode=$(stat -c %a -- "$_p")
_uid=$(stat -c %u -- "$_p")
_gid=$(stat -c %g -- "$_p")
_val=$(sha256sum -- "$_p" | awk '{print $1}')
printf '%s\tf\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
done < <(find ./etc -type f -print0)
# symlinks
while IFS= read -r -d '' _p; do
_mode=$(stat -c %a -- "$_p")
_uid=$(stat -c %u -- "$_p")
_gid=$(stat -c %g -- "$_p")
_val=$(readlink -- "$_p")
printf '%s\tl\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
done < <(find ./etc -type l -print0)
sort -o "${_out}" "${_out}" 2>/dev/null || true
}
leaf_conffile_is_unmodified() {
# usage: leaf_conffile_is_unmodified <abs_path> <trace_dir>
# return 0 if unmodified (safe to remove), 1 otherwise
local path="$1" tdir="$2"
local cf="${tdir}/CONFFILES"
local line type val mode uid gid
local cur_val cur_mode cur_uid cur_gid
[ -r "${cf}" ] || return 1
[ -e "${path}" ] || return 1
line="$(grep -F -m1 -- "${path}"$'\t' "${cf}" 2>/dev/null)" || return 1
IFS=$'\t' read -r _p type val mode uid gid <<< "${line}"
cur_mode="$(stat -c %a -- "${path}" 2>/dev/null)" || return 1
cur_uid="$(stat -c %u -- "${path}" 2>/dev/null)" || return 1
cur_gid="$(stat -c %g -- "${path}" 2>/dev/null)" || return 1
# metadata changed -> treat as modified
if [ "${cur_mode}" != "${mode}" ] || [ "${cur_uid}" != "${uid}" ] || [ "${cur_gid}" != "${gid}" ]; then
return 1
fi
case "${type}" in
f)
cur_val="$(sha256sum -- "${path}" 2>/dev/null | awk '{print $1}')" || return 1
[ "${cur_val}" = "${val}" ] && return 0 || return 1
;;
delete)
sed -i "/${_item}/d" "${INSTALLED_PACKAGES}"
l)
cur_val="$(readlink -- "${path}" 2>/dev/null)" || return 1
[ "${cur_val}" = "${val}" ] && return 0 || return 1
;;
*)
return 1
;;
esac
}
leaf_update_package_database() {
local action=$1 atom=$2 db="${INSTALLED_PACKAGES}"
local tmp="${db}.tmp.$$"
case "$action" in
add)
{ [ -f "$db" ] && grep -Fvx -- "$atom" "$db" || :; printf '%s\n' "$atom"; } >"$tmp" &&
mv -f -- "$tmp" "$db"
;;
delete)
grep -Fvx -- "$atom" "$db" >"$tmp" &&
mv -f -- "$tmp" "$db"
;;
*)
leaf_error "unknown operation leaf_update_package_database ${action}."
;;
esac
}
leaf_update_environment() {
ldconfig
[ ! -r /etc/profile ] || source /etc/profile
}
leaf_run_phase() {
# usage: leaf_run_phase <phase_name> <func_name>
local phase="$1" fn="$2"
LEAF_PHASE_RC=0
declare -F "$fn" >/dev/null || {
LEAF_PHASE_RC=127
return 0
}
if leaf_is_noop_func "$fn"; then
LEAF_PHASE_RC=0
return 0
fi
leaf_set_title "leaf: ${PKG_PREFIX}/${PKG_NAME} (${phase})"
echo -e "${GREEN_COLOR}>>> ${PKG_PREFIX}/${PKG_NAME}: ${phase}${CLEAR_COLOR}"
set +e
(
set -eE -o pipefail
"$fn"
)
LEAF_PHASE_RC=$?
set -e
return 0
}
leaf_phase_must() {
local phase="$1" fn="$2" msg="$3"
leaf_run_phase "$phase" "$fn"
if [ "${LEAF_PHASE_RC}" -ne 0 ]; then
leaf_error "${msg:-${fn} failed (rc=${LEAF_PHASE_RC})}"
fi
}
leaf_phase_try() {
# just run, caller checks $LEAF_PHASE_RC
leaf_run_phase "$1" "$2"
}
leaf_prepare_package() {
local url sourcefile index _md5sum local_src pkg_src_root
local url sourcefile index _sum local_src
local checksum_algo checksum_arr_name
local decl
leaf_set_title "leaf: ${PKG_PREFIX}/${PKG_NAME} (Fetching)"
distdir="${DIST_DIR}"
filedir="${PKGBUILD_DIR}/${PKG_PREFIX}/${pkgname}"
mkdir -pv -- "${distdir}"
pkg_src_root="${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
# choose checksum type (sha512 -> sha256 -> md5)
if declare -p sha512sums &>/dev/null; then
decl="$(declare -p sha512sums 2>/dev/null)"
[[ "${decl}" == "declare -a "* || "${decl}" == "declare -a"* ]] \
|| leaf_error "sha512sums is defined but is not an array"
checksum_algo="sha512"
checksum_arr_name="sha512sums"
elif declare -p sha256sums &>/dev/null; then
decl="$(declare -p sha256sums 2>/dev/null)"
[[ "${decl}" == "declare -a "* || "${decl}" == "declare -a"* ]] \
|| leaf_error "sha256sums is defined but is not an array"
checksum_algo="sha256"
checksum_arr_name="sha256sums"
elif declare -p md5sums &>/dev/null; then
decl="$(declare -p md5sums 2>/dev/null)"
[[ "${decl}" == "declare -a "* || "${decl}" == "declare -a"* ]] \
|| leaf_error "md5sums is defined but is not an array"
checksum_algo="md5"
checksum_arr_name="md5sums"
else
leaf_error "no checksum list defined: need one of sha512sums/sha256sums/md5sums"
fi
local -n checksums_ref="${checksum_arr_name}"
# enter ${distdir}
pushd "${distdir}" >/dev/null || leaf_error "cannot enter distdir: ${distdir}"
for index in "${!sources[@]}"; do
@@ -424,12 +676,17 @@ leaf_prepare_package() {
if [ -f "${sourcefile}" ]; then
echo "${sourcefile} exists, checking..."
_md5sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')"
if [ "${_md5sum}" = "${md5sums[$index]}" ]; then
echo "md5sum match, skip ${sourcefile}."
case "${checksum_algo}" in
sha512) _sum="$(sha512sum -- "${sourcefile}" | awk '{print $1}')" ;;
sha256) _sum="$(sha256sum -- "${sourcefile}" | awk '{print $1}')" ;;
md5) _sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')" ;;
*) leaf_error "internal error: unknown checksum algo: ${checksum_algo}" ;;
esac
if [ "${_sum}" = "${checksums_ref[$index]}" ]; then
echo "${checksum_algo}sum match, skip ${sourcefile}."
continue
fi
echo "md5sum does not match, refetching..."
echo "${checksum_algo}sum does not match, refetching..."
rm -f -- "${sourcefile}"
fi
@@ -439,37 +696,66 @@ leaf_prepare_package() {
wget -O "${sourcefile}" -- "${url}" || leaf_error "wget failed: ${url}"
;;
*)
local_src="${pkg_src_root}/${url}"
local_src="${filedir}/${url}"
echo "Copying ${sourcefile} from local file ${local_src}..."
[ -f "${local_src}" ] || leaf_error "local source not found: ${local_src}"
cp -f -- "${local_src}" "${sourcefile}" || leaf_error "cp failed: ${local_src}"
;;
esac
_md5sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')"
if [ "${_md5sum}" != "${md5sums[$index]}" ]; then
leaf_error "md5sums of ${sourcefile}(${_md5sum}) does not match with ${md5sums[$index]}!"
case "${checksum_algo}" in
sha512) _sum="$(sha512sum -- "${sourcefile}" | awk '{print $1}')" ;;
sha256) _sum="$(sha256sum -- "${sourcefile}" | awk '{print $1}')" ;;
md5) _sum="$(md5sum -- "${sourcefile}" | awk '{print $1}')" ;;
*) leaf_error "internal error: unknown checksum algo: ${checksum_algo}" ;;
esac
if [ "${_sum}" != "${checksums_ref[$index]}" ]; then
leaf_error "${checksum_algo}sum of ${sourcefile}(${_sum}) does not match with ${checksums_ref[$index]}!"
fi
done
popd >/dev/null || true
echo "Sources ready."
src_prepare
leaf_phase_must "Preparing" src_prepare "Prepare failed."
}
leaf_build_package() {
leaf_prepare_package
src_build
rm -rf -- ${TMPDIR} ${HOME}
mkdir -pv ${TMPDIR}
mkdir -pv ${HOME}
if ! src_check; then
if [ "${FORCE_INSTALL}" == "1" ]; then
leaf_prepare_package
leaf_phase_must "Building" src_build "Build failed."
leaf_phase_try "Checking" src_check
if [ "${LEAF_PHASE_RC}" -ne 0 ]; then
if [ "${FORCE_INSTALL:-0}" = "1" ]; then
echo -e "${RED_COLOR}* Tests failed, but is in force-install mode.${CLEAR_COLOR}"
else
leaf_error "Tests failed, please check. Aborting installation."
fi
fi
src_install
## run tests (strict: any failing command inside src_check fails the whole check)
#local _check_rc=0
#set +e
#( set -e; src_check )
#_check_rc=$?
#set -e
#
#if [ "${_check_rc}" -ne 0 ]; then
# if [ "${FORCE_INSTALL}" = "1" ]; then
# echo -e "${RED_COLOR}* Tests failed, but is in force-install mode.${CLEAR_COLOR}"
# else
# leaf_error "Tests failed, please check. Aborting installation."
# fi
#fi
# ensure pkgdir exists
rm -rf -- "${pkgdir}"
install -dm755 -- "${pkgdir}"
leaf_phase_must "Installing" src_install "Installing failed."
local _option
for _option in $(echo ${!BUILD_OPTIONS[*]}); do
if [[ x"${_option}" == x"strip" ]] && [[ x"${BUILD_OPTIONS[$_option]}" == x"1" ]]; then
@@ -493,9 +779,9 @@ leaf_install_package() {
# trace need to to in the same fakeroot
leaf_trace_package "${pkgdir}"
# ROOT starts
src_preinstall
leaf_phase_must "Preinstall" src_preinstall "Preinstall failed."
leaf_merge_package "${pkgdir}"
src_postinstall
leaf_phase_must "Postinstall" src_postinstall "Postinstall failed."
leaf_update_package_database add "${PKG_PREFIX}/${PKG_NAME}"
leaf_update_environment
# ROOT ends
@@ -508,9 +794,9 @@ leaf_dirct-install_package() {
# trace need to to in the same fakeroot
leaf_trace_package "${pkgdir}"
# ROOT starts
src_preinstall
leaf_phase_must "Preinstall" src_preinstall "Preinstall failed."
leaf_merge_package "${pkgdir}"
src_postinstall
leaf_phase_must "Postinstall" src_postinstall "Postinstall failed."
leaf_update_package_database add "${PKG_PREFIX}/${PKG_NAME}"
leaf_update_environment
# ROOT ends
@@ -522,39 +808,207 @@ leaf_force-install_package(){
leaf_install_package
}
leaf_upgrade_package() {
local old_query="$1" new_query="$2"
local old_prefix old_name new_prefix new_name
local new_srcdir new_pkgdir
local old_trace_dir new_trace_dir
local skipfile
# -------- resolve OLD (installed) --------
leaf_find_remove_pkgbuild "${old_query}"
old_prefix="${PKG_PREFIX}"
old_name="${PKG_NAME}"
old_trace_dir="${TRACE_DIR}/${old_prefix}/${old_name}"
OLD_TRACE_DIR="${old_trace_dir}"
# -------- resolve NEW (available) --------
leaf_find_pkgbuild "${new_query}"
new_prefix="${PKG_PREFIX}"
new_name="${PKG_NAME}"
# sanity
if [[ "${old_prefix}/${old_name}" == "${new_prefix}/${new_name}" ]]; then
leaf_error "upgrade: old and new refer to the same package: ${old_prefix}/${old_name}"
fi
# =========================================================
# 1) BUILD NEW (no ROOT touch)
# =========================================================
PKG_PREFIX="${new_prefix}"
PKG_NAME="${new_name}"
leaf_reset_state
leaf_message_init
source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD"
leaf_parse_options
srcdir="${BUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}"
LEAFTMPDIR="/tmp/leaf-tmp/${PKG_NAME}"
LEAFHOME="${LEAFTMPDIR}"
export TMPDIR="${LEAFTMPDIR}"
export HOME="${LEAFHOME}"
pkgdir="${srcdir}/__pkgdir__"
rm -rf -- "${srcdir}" && install -dm755 -- "${srcdir}" && cd "${srcdir}"
mkdir -pv -- "${LEAFTMPDIR}" "${LEAFHOME}"
leaf_build_package
new_srcdir="${srcdir}"
new_pkgdir="${pkgdir}"
# =========================================================
# 2) INSTALL NEW (touch ROOT)
# Use dirct-install path so we don't rebuild.
# =========================================================
PKG_PREFIX="${new_prefix}"
PKG_NAME="${new_name}"
leaf_reset_state
leaf_message_init
source "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD"
leaf_parse_options
srcdir="${new_srcdir}"
pkgdir="${new_pkgdir}"
LEAFTMPDIR="/tmp/leaf-tmp/${PKG_NAME}"
LEAFHOME="${LEAFTMPDIR}"
export TMPDIR="${LEAFTMPDIR}"
export HOME="${LEAFHOME}"
[ -d "${pkgdir}" ] || leaf_error "upgrade: built pkgdir missing: ${pkgdir}"
cd "${srcdir}" || leaf_error "upgrade: cannot enter build dir: ${srcdir}"
leaf_dirct-install_package
#rm -rf -- "${LEAFTMPDIR}" "${LEAFHOME}"
# new trace dir now exists (generated by leaf_dirct-install_package -> leaf_trace_package)
new_trace_dir="${TRACE_DIR}/${new_prefix}/${new_name}"
[ -d "${new_trace_dir}" ] || leaf_error "upgrade: new trace dir missing: ${new_trace_dir}"
rm -rf "${LEAFHOME}"
rm -rf "${LEAFTMPDIR}"
# =========================================================
# 3) REMOVE OLD, but SKIP paths owned by NEW
# =========================================================
skipfile="$(mktemp "${TEMP_DIR}/leaf-upgrade-skip.XXXXXX")" || leaf_error "mktemp failed"
cat "${new_trace_dir}/FILES" "${new_trace_dir}/LINKS" "${new_trace_dir}/DIRS" > "${skipfile}" || {
rm -f -- "${skipfile}"
leaf_error "upgrade: cannot create skiplist"
}
PKG_PREFIX="${old_prefix}"
PKG_NAME="${old_name}"
leaf_reset_state
leaf_message_init
# old remove sources PKGBUILD from TRACE (best effort)
source "${old_trace_dir}/PKGBUILD" 2>/dev/null || true
export LEAF_REMOVE_SKIPLIST="${skipfile}"
leaf_remove_package
unset LEAF_REMOVE_SKIPLIST
rm -f -- "${skipfile}"
echo -e "${GREEN_COLOR}* Upgraded ${old_prefix}/${old_name} -> ${new_prefix}/${new_name}${CLEAR_COLOR}\n"
}
leaf_in_skiplist() {
# usage: leaf_in_skiplist <abs_path>
# return 0 if path in LEAF_REMOVE_SKIPLIST file
local p="$1"
[ -n "${LEAF_REMOVE_SKIPLIST:-}" ] || return 1
[ -r "${LEAF_REMOVE_SKIPLIST}" ] || return 1
grep -Fxq -- "$p" "${LEAF_REMOVE_SKIPLIST}" 2>/dev/null
}
leaf_remove_package() {
[ -n "$(grep "${PKG_PREFIX}/${PKG_NAME}" ${INSTALLED_PACKAGES})" ] || {
leaf_error "Package ${PKG_PREFIX}/${PKG_NAME} is NOT installed"
}
src_preremove
leaf_phase_must "Preremove" src_preremove "Preremove failed."
local _file _link _directory _etc_backup_path _relative_path _backup_etc=false
_etc_backup_path="/etc/.leaf_backup/${PKGNAME}_$(date +%Y%m%d%H%M%S)"
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/FILES" | while read -r _file; do
if [[ "${_file}" == /etc/* && -f "${_file}" ]]; then
local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
local _has_conffiles=0
[ -r "${_trace_dir}/CONFFILES" ] && _has_conffiles=1
_etc_backup_path="/etc/.leaf_backup/${PKG_NAME}_$(date +%Y%m%d%H%M%S)"
# FILES
while read -r _file; do
if leaf_in_skiplist "${_file}"; then
continue
fi
if [[ "${_file}" == /etc/* && -e "${_file}" ]]; then
if [ "${_has_conffiles}" -eq 1 ]; then
if leaf_conffile_is_unmodified "${_file}" "${_trace_dir}"; then
rm -fv -- "${_file}"
else
leaf_record_message "Preserved modified config file: ${_file}"
fi
continue
fi
# fallback for old packages without CONFFILES (keep old behavior)
if [[ -f "${_file}" ]]; then
_backup_etc=true
_relative_path="${_file#/etc/}"
mkdir -pv "${_etc_backup_path}/$(dirname "${_relative_path}")"
mv -v "${_file}" "${_etc_backup_path}/${_relative_path}"
mv -v -- "${_file}" "${_etc_backup_path}/${_relative_path}"
else
rm -fv "${_file}"
rm -fv -- "${_file}"
fi
done
else
rm -fv -- "${_file}"
fi
done < "${_trace_dir}/FILES"
if [[ "${_backup_etc}" == true ]]; then
leaf_record_message "Found files in /etc, backuped to ${_etc_backup_path}."
fi
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/LINKS" | while read -r _link; do
[ ! -L "${_link}" ] || rm -fv "${_link}"
done
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/DIRS" | while read -r _directory; do
if [[ -d _directory ]]; then
# LINKS
while read -r _link; do
if leaf_in_skiplist "${_link}"; then
continue
fi
if [[ "${_link}" == /etc/* && -e "${_link}" ]]; then
if [ "${_has_conffiles}" -eq 1 ]; then
if leaf_conffile_is_unmodified "${_link}" "${_trace_dir}"; then
rm -fv -- "${_link}"
else
leaf_record_message "Preserved modified config link: ${_link}"
fi
continue
fi
else
[ ! -L "${_link}" ] || rm -fv -- "${_link}"
fi
done < "${_trace_dir}/LINKS"
# DIRS
while read -r _directory; do
if leaf_in_skiplist "${_directory}"; then
continue
fi
if [[ -d "${_directory}" ]]; then
rmdir --ignore-fail-on-non-empty "${_directory}"
fi
done
src_postremove
done < "${_trace_dir}/DIRS"
leaf_phase_must "Postremove" src_postremove "Postremove failed."
leaf_invoke_hooks remove
rm -rf "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
rm -rf "${_trace_dir}"
leaf_update_package_database delete "${PKG_PREFIX}/${PKG_NAME}"
leaf_update_environment
echo -e "${GREEN_COLOR}<<< removed ${PKG_PREFIX}/${PKG_NAME}${CLEAR_COLOR}"
}
leaf_search_package() {
@@ -577,6 +1031,40 @@ leaf_show_package() {
echo -e "License: ${license[@]}\n"
}
leaf_query() {
local q="$1"
local trace_root="${TRACE_DIR:-/var/lib/leaf/trace}"
if ! leaf_find_remove_pkgbuild "$q"; then
leaf_error "${q} cannot found in the leaf installed database."
fi
local pkgbuild_path="${trace_root}/${PKG_PREFIX}/${PKG_NAME}/PKGBUILD"
[[ -f "$pkgbuild_path" ]] || leaf_error "query: missing PKGBUILD: $pkgbuild_path"
[[ -r "$pkgbuild_path" ]] || leaf_error "query: PKGBUILD not readable: $pkgbuild_path"
(
set -euo pipefail
unset pkgname pkgver
# shellcheck disable=SC1090
source "$pkgbuild_path"
local full="${PKG_PREFIX}/${PKG_NAME}"
if [[ -t 1 && -z "${NO_COLOR-}" ]]; then
local BOLD=$'\e[1m'
printf '%s%s%s %s %s%s%s\n' \
"${BOLD}" "${full}" "${CLEAR_COLOR}" \
"${pkgname}" \
"${GREEN_COLOR}" "${pkgver}" "${CLEAR_COLOR}"
else
printf '%s %s %s\n' "${full}" "${pkgname}" "${pkgver}"
fi
)
}
leaf_pack_package() {
leaf_build_package
pushd "${srcdir}" > /dev/null 2>&1