diff options
author | Daniel Friesel <derf@derf.homelinux.org> | 2009-07-11 01:17:18 +0200 |
---|---|---|
committer | Daniel Friesel <derf@derf.homelinux.org> | 2009-07-11 01:21:48 +0200 |
commit | 324568044af4287dd25a90dc143ea26df565d1e3 (patch) | |
tree | 0bcf9ff698d825c50a72d85c02560d88782e1915 /bin/pkg | |
parent | 389bd0bcb530fee9c92ed9c419d82303a322274d (diff) |
Renamed pkg to caretaker
Diffstat (limited to 'bin/pkg')
-rwxr-xr-x | bin/pkg | 854 |
1 files changed, 0 insertions, 854 deletions
diff --git a/bin/pkg b/bin/pkg deleted file mode 100755 index 3441c0a..0000000 --- a/bin/pkg +++ /dev/null @@ -1,854 +0,0 @@ -#!/usr/bin/env zsh -## pkg - /home package manager and zsh playground -## Copyright © 2008-2009 by Daniel Friesel <derf@derf.homelinux.org> -## -## Permission to use, copy, modify, and/or distribute this software for any -## purpose with or without fee is hereby granted, provided that the above -## copyright notice and this permission notice appear in all copies. -## -## THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -## WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -## MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -## ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -## WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -## ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -## OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -setopt extended_glob -typeset -a -U triggers -typeset -a CL_OPTIONS - -c_info=$'\e[0;36m' -c_error=$'\e[0;31m' -c_reset=$'\e[0m' - -function info { - echo -ne "${c_info}$*${c_reset}" -} - -function warn { - echo -ne "${c_error}$*${c_reset}" > /dev/stderr -} - -function die { - echo -ne "${c_error}$*${c_reset}" > /dev/stderr - exit 100 -} - -function say { - echo $* -} - -function clear_line { - echo -ne "\r\e[2K" -} - -# Read local configuration -: ${XDG_CONFIG_HOME=$HOME/.config} -if [[ -r $XDG_CONFIG_HOME/pkg/pkg.conf ]] { - source $XDG_CONFIG_HOME/pkg/pkg.conf -} elif [[ -r $HOME/.pkg.conf ]] { - source $HOME/.pkg.conf -} - -# Parse commandline options -while [[ $1 == --* ]] { - case $1 in - --coluors) COLOURS=1 ;; - --no-colours) COLOURS=0 ;; - --quiet) SILENT=1 ;; - --no-quiiet) SILENT=0 ;; - --debug) DEBUG=1 ;; - --no-debug) DEBUG=0 ;; - --auto-update) AUTOUPDATE=1 ;; - --no-auto-update) AUTOUPDATE=0 ;; - --checklinks-options) CL_OPTIONS+=$2; shift ;; - --packagedir) PKG_DIR=$2; shift ;; - --packageroot) PKG_ROOT=$2; shift ;; - --progress) PROGRESS=1 ;; - --no-progress) PROGRESS=0 ;; - *) die "Unknown argument: '$1'\n" ;; - esac - shift -} - -action=$1 -((#)) && shift - -[[ -n $PKG_ROOT ]] || die "No PKG_ROOT specified. Please edit your pkg.conf\n" -: ${PKG_DIR:="$HOME/packages"} -: ${PKGLIST_LOCAL=0} -: ${CL_OPTIONS:=--quiet} -: ${SILENT=0} -: ${DEBUG=0} -: ${AUTOUPDATE=1} -: ${GIT_USE_ORIGIN=1} -: ${COLOURS=1} -: ${PROGRESS=1} -export PKG_DIR -export PKG_ROOT - -function debug { - typeset func line - if (( $# >= 3 )) { - func=$1 - line=$2 - shift 2 - } - echo "(debug) $func:$line: $*" >&2 -} - -# I need function name and line number of the function _calling_ debug, -# so I can't get them from inside the debug function. -alias debug='debug ${(z%):-%N %i}' - -# Avoid calling debug without debug mode... just in case one needs more speed -if (( !DEBUG )) { - unalias debug - function debug {} -} - -if (( SILENT )) { - # The goal is not to override anything set by the user... - # So, an alias should be safer than fiddling with $MAKEFLAGS - alias make='make -s' - function info {} - function say {} - function clear_line {} - PROGRESS=0 -} - -if (( !COLOURS )) { - c_info='' - c_reset='' - c_error='' -} - -if [[ ! -d $PKG_DIR ]] { - die "Error: Package directory '$PKG_DIR' not found\n" -} - - -## -## Setup some additional variables related to PKG_ROOT -## - -# Protocol -if [[ $PKG_ROOT == ssh://* ]] { - PKG_PROTO='ssh' -} elif [[ $PKG_ROOT == git://* ]] { - PKG_PROTO='git' -} elif [[ $PKG_ROOT == /* ]] { - PKG_PROTO='file' -} else { - die "Error: Unknown protocol in PKG_ROOT '$PKG_ROOT'\n" -} - -# user, host, path -if [[ $PKG_PROTO == (git|ssh) ]] { - PKG_HOST=${${PKG_ROOT#"${PKG_PROTO}://"}%%/*} - PKG_PATH=${PKG_ROOT#"${PKG_PROTO}://$PKG_HOST"} - if [[ $PKG_HOST == *@* ]] { - PKG_USER=${PKG_HOST%%@*} - PKG_HOST=${PKG_HOST#*@} - } else { - PKG_USER=$USERNAME - } -} elif [[ $PKG_PROTO == 'file' ]] { - PKG_PATH=$PKG_ROOT -} - -: ${PKGLIST_PATH:=$PKG_PATH/pkglist} - -if ((DEBUG)) { - info "pkg: running in debug mode. Infos follow:\n" - echo "--- running ---" - echo " zsh $ZSH_VERSION" - echo " git "${$(git --version)[3]} - echo " pkg "${$(git --git-dir=$PKG_DIR/${${(s:/:)$(readlink $0)}[-3]}/.git/ log -n 1)[2]} - echo "--- settings ---" - echo " PKG_ROOT $PKG_ROOT" - echo " PKG_PROTO $PKG_PROTO" - echo " PKG_USER $PKG_USER" - echo " PKG_HOST $PKG_HOST" - echo " PKGLIST_PATH $PKGLIST_PATH" - echo " PKGLIST_LOCAL $PKGLIST_LOCAL" - echo " PKG_DIR $PKG_DIR" - echo " CL_OPTIONS $CL_OPTIONS" - echo " SILENT $SILENT" - echo " COLOURS $COLOURS" - echo " PROGRESS $PROGRESS" - echo " AUTOUPDATE $AUTOUPDATE" - echo " GIT_USE_ORIGIN $GIT_USE_ORIGIN" -} - -function check_installed { - [[ -n $1 && -d $PKG_DIR/$1 ]] || die "Package not installed: '$1'\n" -} - -function check_valid { - list_exists $1 || die "Package does not exist: '$1'\n" -} - -# Default reply: Yes -function confirm_yes { - echo -n "$* [Y/n] " - read -k 1 - [[ $REPLY != $'\n' ]] && echo - [[ $REPLY == (y|Y|$'\n') ]] -} - -# Default reply: No -function confirm_no { - echo -n "$* [y/N] " - read -q -} - - -## -## Major internal functions -## - -# this function only has content when wrap is used. -# but since it's always called, it will be empty by default -function wrap_info {} - -function progress { - ((PROGRESS)) || return - typeset -i current=$1 - typeset -i max=$2 - typeset desc=$3 - typeset desc2=$4 - typeset output='' - typeset -i currentper=$(( (current*100)/max )) - typeset item j c a - function item { - for j in {0..$1}; { - (( j > 0 )) && output+=$2 - } - } - c=$(( currentper/5 )) - a=$(( 20-c )) - output+="${c_info}$desc${c_reset} [" - item $c '=' - item $a ' ' - output+="] $currentper% $desc2" - clear_line - echo -ne $output -} - -## VCS Wrappers - -function vcs_setup { - cd $PKG_DIR/$1 -} - -function vcs_to_list ( - vcs_setup $1 - if [[ -d $PKG_DIR/$1/.git ]] { - echo -n "$1 git " - echo ${$(git log -n 1 master)[2]} - } else { - warn "No git repository found: $1\n" - } -) - -function vcs_add ( - if [[ $(list_type $1) == git ]] { - git clone "$PKG_ROOT/$1" - vcs_setup $1 - git config push.default matching - } else { - die "$1: Cannot handle repository format '$(list_type $1)'\n" - } -) - -function vcs_log ( - vcs_setup $1 - git log -) - -function vcs_branch_is_master ( - vcs_setup $1 - typeset IFS=$'\n' branch line - for line in $(git branch); { - [[ $line == \*\ * ]] && branch=${line#* } - } - if [[ $branch != master ]] { - warn "$1: The currently checked out branch is not master, but '$branch'\n" \ - "Currently, with GIT_USE_ORIGIN=0, pkg can only operate on the branch master\n" \ - " -> skipping repo, please fix manually\n" - return 1 - } -) - -function vcs_pull ( - vcs_setup $1 - if ((GIT_USE_ORIGIN)) { - # the package might be newly created and not have an origin yet - vcs_fix_origin $1 - git pull - } else { - vcs_branch_is_master $1 && git pull $PKG_ROOT/${PWD:t} master - } -) - -function vcs_push ( - vcs_setup $1 - if ((GIT_USE_ORIGIN)) { - # see above - vcs_fix_origin $1 - git push - } else { - vcs_branch_is_master $1 && git push $PKG_ROOT/${PWD:t} master - } -) - -function vcs_status ( - vcs_setup $1 - git status -) - -function vcs_fix_origin ( - vcs_setup $1 - if [[ ! -r .git/remotes/origin && ! -r .git/refs/remotes/origin/HEAD ]] { - fgrep -q '[remote "origin"]' .git/config || - git remote add origin $PKG_ROOT/$1 - } -) - - -## List stuff - -function list_is_installed { - grep -q "^$1 " $PKG_DIR/.list -} - -function list_exists { - grep -q "^$1 " $PKG_DIR/.list-remote -} - -function list_packages_local { - cut -d ' ' -f 1 $PKG_DIR/.list -} - -function list_packages_remote { - cut -d ' ' -f 1 $PKG_DIR/.list-remote -} - -function list_incoming { - [[ $(list_version_local $1) != $(list_version_remote $1) ]] -} - -function list_type { - echo ${$(grep "^$1 " $PKG_DIR/.list-remote)[2]} -} - -function list_type_local { - echo ${$(grep "^$1 " $PKG_DIR/.list)[2]} -} - -function list_update_remote { - typeset tmpfile=$(mktemp -t pkglist.XXXXXX) - typeset -i ret=0 - export PKG_DIR - if [[ $PKGLIST_LOCAL == 1 || $PKG_PROTO == 'file' ]] { - $PKGLIST_PATH $PKG_PATH > $tmpfile - } elif [[ $PKG_PROTO == 'ssh' ]] { - ssh $PKG_USER@$PKG_HOST "PKG_DIR='$PKG_DIR' $PKGLIST_PATH $PKG_PATH" > $tmpfile - } - if [[ -n $(cat $tmpfile) ]] { - cp $tmpfile .list-remote - } else { - die "remote list update failed\n" - } - rm $tmpfile -} - -function list_update_local { - typeset all=${#$(echo $PKG_DIR/*(/))} - typeset -i current=0 - typeset i - rm -f $PKG_DIR/.list - for package in *(-/); { - (( current++ )) - progress $current $all 'Updating package list' $package - vcs_to_list $package >> $PKG_DIR/.list - } -} - -function list_package_update { - typeset list - list=$(grep -v "^$1 " $PKG_DIR/.list) - echo $list > $PKG_DIR/.list - vcs_to_list $1 >> $PKG_DIR/.list -} - -function list_package_remove { - typeset list - list=$(grep -v "^$1 " $PKG_DIR/.list) - echo $list > $PKG_DIR/.list -} - -function list_version_local { - echo ${$(grep "^$1 " $PKG_DIR/.list)[3]} -} - -function list_version_remote { - echo ${$(grep "^$1 " $PKG_DIR/.list-remote)[3]} -} - - -function priority_name { - case $1 in - 6) echo 'essential' ;; - 5) echo 'important' ;; - 4) echo 'required' ;; - 3) echo 'standard' ;; - 2) echo 'optional' ;; - 1) echo 'extra' ;; - *) warn "invalid priority: $!" ;; - esac -} - -function exec_hook { - typeset package=$1 - typeset hook=$2 - if [[ -r $PKG_DIR/$package/hooks/$hook ]] { - info "$package: executing hook $hook\n" - cd $PKG_DIR/$package - (source hooks/$hook) - } -} - -function global_hook { - cd $PKG_DIR/$1 - case $2 in - post-add) - exec_hook $1 post-add - global_hook $1 post-update - ;; - pre-update) - ;; - post-update) - triggers+=$1 - check_prereqs $1 - if [[ -r Makefile ]] { - wrap_info $1 - info "Running make\n" - make - } - checklinks $CL_OPTIONS \ - --parameter package=${${PWD#$HOME}#/##} \ - --parameter etc=${${PWD#$HOME}#/##}/etc - populate_collected $1 - update_provides $1 - list_package_update $1 - ;; - pre-remove) - exec_hook $1 pre-remove - genocide_collected $1 - checklinks $CL_OPTIONS --remove - list_package_remove $1 - update_provides $1 - ;; - esac - (( $+functions[pkg_hook_$2] )) && pkg_hook_$2 $1 -} - -function check_prereqs { - typeset -a -U install maybe_install - typeset warn info i - typeset package=$1 - [[ -r $PKG_DIR/$package/prereqs ]] || return 0 - cd $PKG_DIR/$package - wrap_info $1 - info "checking prerequisites\n" - - # function scope → typeset and localoptions are possible - function source_prereqs { - { - source prereqs - } always { - if (( TRY_BLOCK_ERROR )) { - warn "Error in prereqs script\n" - TRY_BLOCK_ERROR=0 - } - } - } - - function is_installed { - [[ -d $PKG_DIR/$1 ]] - } - function perlmodule { - perl -M$1 < /dev/null 2> /dev/null - } - function executable file_in_path { - if [[ $0 == file_in_path ]] { - warn "'file_in_path' is deprecated, use 'executable' instead\n" - } - which $1 > /dev/null - } - function offer_install { - install+=$1 - } - function depend require { - if [[ $0 == require ]] { - warn "'require' is depracated, use 'depend' instead\n" - } - if [[ $1 == 'package' ]] { - is_installed $2 || offer_install $2 - } else { - $* || warn+="Requirement failed: $*\n" - } - } - - function suggest { - if [[ $1 == 'package' ]] { - is_installed $2 || info "$package suggests package $2\n" - } else { - $* || echo "Suggest failed: $*\n" - } - } - - function recommend { - if [[ $1 == 'package' ]] { - is_installed $2 || maybe_install+=$2 - } else { - $* || info+="Recommend failed: $*\n" - } - } - - source_prereqs - - if [[ -n $warn || -n $info ]] { - [[ -n $warn ]] && warn $warn - [[ -n $info ]] && echo -n $info - read -q '?continue [] ' - } - - if [[ -n $install ]] { - info "$1 requires the following packages: ${(j:, :)install}\n" - if confirm_yes "Install them?"; then - for package in $install; { - pkg_add $package - } - fi - } - if [[ -n $maybe_install ]] { - info "$1 recommends the following packages: ${(j:, :)maybe_install}\n" - if confirm_no "Install them?"; then - for package in $maybe_install; { - pkg_add $package - } - fi - } -} - -# Write a package's documentation to .collected -# and symlink its binaries from ~/bin -function populate_collected { - cd $PKG_DIR/$1 || return - typeset -i bins=0 bino=0 mans=0 mano=0 - typeset man section manpage file - - wrap_info $1 - info "Enabling documentation\n" - for man in man/*/*(N); { - section=${man:h:t} - manpage=${man:t} - if podchecker man/$section/$manpage &> /dev/null; then - pod2man -u -s $section -c "$1 package" -r $HOME man/$section/$manpage > $PKG_DIR/.collected/man/man$section/${manpage%.pod}.$section - fi - } - for file in bin/*(N); { - if podchecker $file &> /dev/null; then - pod2man -u $file > $PKG_DIR/.collected/man/man1/${file:t}.1 - fi - } - for file in bin/*(-*N); { - if [[ -L $HOME/$file || ! -e $HOME/$file ]] { - if [[ $(readlink $HOME/$file) != (../${PKG_DIR//$HOME\/}|$PKG_DIR)/$1/$file ]] { - rm -f $HOME/$file - if [[ $PKG_DIR == ${HOME}* ]] { - ln -s ../${PKG_DIR//$HOME\/}/$1/$file $HOME/$file - } else { - ln -s $PKG_DIR/$1/$file $HOME/$file - } - } - } else { - warn "populate_collected: Not updating ~/$file since it's not a symlink\n" - } - } -} - -# Remove a package's files from .collected -# Assuming there are no packages with colliding files -function genocide_collected { - typeset i file man manual section - cd $PKG_DIR/$1 || return - wrap_info $1 - info "Removing documentation" - for man in man/*/*(N); { - section=${man:h:t} - manual=${man:t} - if [[ -e $PKG_DIR/.collected/man/man$section/$manual.$section ]] { - rm $PKG_DIR/.collected/man/man$section/$manual.$section - } - } - for file in bin/*(N); { - rm -f $PKG_DIR/.collected/man/man1/${file:t}.1 - } - clear_line - for file in bin/*(-*N); { - if [[ $(readlink $HOME/$file) == (../${PKG_DIR//$HOME\/}|$PKG_DIR)/$1/$file ]] { - rm -f $HOME/$file - } - } -} - -function update_provides { - typeset package - for package in $PKG_DIR/$1/provides/*(N:t); { - if [[ -d $PKG_DIR/$package ]] { - triggers+=$package - } - } -} - -function apply_triggers { - typeset package - for package in $triggers; { - exec_hook $package 'post-update' - } -} - -# Iterate a function over every installed package -function wrap { - typeset function=$1 - typeset arg=$2 - typeset progress=$3 - typeset i - typeset -i all current - - if [[ -n $2 ]] { - $function $2 - } else { - function wrap_info { - clear_line - info "$1: " - } - [[ -n $progress ]] && all=$(list_packages_local | wc -l) - [[ -n $progress ]] && current=0 - for package in *(-/); { - cd $PKG_DIR - (( current++ )) - [[ -n $progress ]] && progress $current $all $progress $package - $function $package - } - [[ -n $progress ]] && clear_line - } -} - -## -## The "frontend" functions -## - -function pkg_add { - if [[ -d $PKG_DIR/$1 ]] { - info "Package '$1' is already installed!\n" - exit 1 - } - check_valid $1 - info "Retrieving package $1...\n" - vcs_add $1 || return 255 - global_hook $1 post-add -} - -function pkg_push { - check_installed $1 - check_valid $1 - if list_incoming $1; then - clear_line - info "Pushing $1\n" - global_hook $1 pre-update - vcs_push $1 - global_hook $1 post-update - fi -} - -function pkg_remove { - check_installed $1 - if [[ -r $PKG_DIR/$1/priority ]] { - if (( $(cat $PKG_DIR/$1/priority) > 3 )) { - confirm_no "Package '$1' is $(priority_name $(cat $PKG_DIR/$1/priority)). Really remove?" || return - } - } - global_hook $1 pre-remove - rm -rf $PKG_DIR/$1 - info "Package removed.\n" -} - -function pkg_upgrade { - check_installed $1 - check_valid $1 - if [[ $(list_type $1) != $(list_type_local $1) ]] { - clear_line - warn "Incompatible systems. Please reinstall: $1\n" - warn " remote '$(list_type $1)' <-> local '$(list_type_local)'\n" - return 9 - } - if list_incoming $1; then - clear_line - info "Updating $1 to $(list_version_remote $1)\n" - global_hook $1 pre-update - vcs_pull $1 - global_hook $1 post-update - fi -} - -function pkg_list { - typeset package crap - case $1 in - ''|local) - list_packages_local - ;; - all|remote) - list_packages_remote - ;; - not-installed) - for package in $(list_packages_remote); { - list_is_installed $package || echo $package - } - ;; - esac -} - -function pkg_status { - typeset vcs_status - check_installed $1 - vcs_status=$(PAGER='' vcs_status $1) - if [[ -n $vcs_status && $vcs_status != *'nothing to commit (working directory clean)' ]] { - if ((SILENT)) { - echo $1 - } else { - clear_line - info "$1:\n" - echo $vcs_status - } - } -} - -function pkg_refresh { - check_installed $1 - global_hook $1 pre-update - global_hook $1 post-update -} - -function pkg_update { - if [[ -z $1 || $1 == local ]] { - info "Updating local package list\n" - list_update_local - clear_line - } - if [[ -z $1 || $1 == remote ]] { - info "Updating remote package list\n" - list_update_remote - } -} - -function pkg_info { - check_valid $1 - - typeset name=$1 - typeset repo_type=$(list_type $1) - typeset priority priority_name - typeset hooks makefile discription state - if [[ -d $1 ]] { - cd $1 - if [[ -r priority ]] { - priority=$(cat priority) - priority_name=$(priority_name $priority) - } - if [[ -d hooks ]] { - hooks=$(ls -m hooks) - } - if [[ -r Makefile ]] { - makefile=1 - } - size=$(du -sh .$(list_type $1) | grep -o '.*[KMG]') - if [[ -r description ]] { - description=$(cat description) - } - state='installed' - if list_incoming $1; then - state+=', needs update' - else - state+=', up-to-date' - fi - } else { - state='not installed' - } - - function show_info { - [[ -z $2 ]] && return - info "$1: " - echo $2 - } - - show_info 'Package' $name - show_info 'State' $state - [[ -n $priority ]] && show_info 'Priority' "$priority ($priority_name)" - show_info 'Local Version' $(list_version_local $1) - show_info 'Remote Version' $(list_version_remote $1) - show_info 'Repository Type' $repo_type - show_info 'Repository Size' $size - show_info 'Hooks' $hooks - show_info 'Description' $description -} - -function pkg_log { - check_installed $1 - vcs_log $1 -} - - -cd $PKG_DIR || die "Cannot cd $PKG_DIR" - -# Note: -# wrap foobar "$1" <- the "" are neccessary here, since $1 is optional (and therefore may be empty) -case $action in - a|add|install) - if [[ $action == install ]] { - warn "pkg install is deprecated, use pkg add instead\n" - } - pkg_add $* - ;; - rm|delete|remove) - if [[ $action == delete ]] { - warn "pkg delete is deprecated, use pkg remove instead\n" - } - pkg_remove $* - ;; - i|info) pkg_info $* ;; - ls|list) pkg_list $* ;; - l|log) pkg_log $* ;; - p|push) - (( AUTOUPDATE )) && pkg_update - wrap pkg_push "$1" 'Pushing' - ;; - r|refresh) wrap pkg_refresh "$1" 'Refreshing' ;; - s|status) wrap pkg_status "$1" 'Checking package status' ;; - u|update) pkg_update $* ;; - f|upgrade|pull) - if [[ $action == upgrade ]] { - warn "pkg updgrade is deprecated, use pkg pull instead\n" - } - (( AUTOUPDATE )) && pkg_update remote - wrap pkg_upgrade "$1" 'Looking for updates' - ;; - e|eval) eval $* ;; - *) die "wait, what?\npkg: unknown action: '$action'\n" ;; -esac - -apply_triggers |