From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id 6401B1381F3 for ; Sat, 22 Jun 2013 15:24:46 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id BFF80E0B07; Sat, 22 Jun 2013 15:24:31 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 39EFDE0B07 for ; Sat, 22 Jun 2013 15:24:26 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 1433C33E469 for ; Sat, 22 Jun 2013 15:24:20 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id E8B2EE5469 for ; Sat, 22 Jun 2013 15:24:17 +0000 (UTC) From: "André Erdmann" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "André Erdmann" Message-ID: <1371823644.f814f32ab17ace769f25ccf80ca77d93a82c3a92.dywi@gentoo> Subject: [gentoo-commits] proj/R_overlay:master commit in: files/shlib/, files/hooks/ X-VCS-Repository: proj/R_overlay X-VCS-Files: files/hooks/git-snapshot.sh files/hooks/mux.sh files/shlib/functions.sh files/shlib/git.sh X-VCS-Directories: files/shlib/ files/hooks/ X-VCS-Committer: dywi X-VCS-Committer-Name: André Erdmann X-VCS-Revision: f814f32ab17ace769f25ccf80ca77d93a82c3a92 X-VCS-Branch: master Date: Sat, 22 Jun 2013 15:24:17 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: 76d29748-742c-4613-aec1-403f4bbda4da X-Archives-Hash: 46d05d5031cbee1b825c6b3cb2ed1076 commit: f814f32ab17ace769f25ccf80ca77d93a82c3a92 Author: André Erdmann mailerd de> AuthorDate: Fri Jun 21 14:07:24 2013 +0000 Commit: André Erdmann mailerd de> CommitDate: Fri Jun 21 14:07:24 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=f814f32a roverlay/files: shlib, hooks This commit adds shell script files for running hooks. shlib/ contains function files and hooks/ contains the actual scripts. --- files/hooks/git-snapshot.sh | 103 +++++++++++++ files/hooks/mux.sh | 25 ++++ files/shlib/functions.sh | 346 ++++++++++++++++++++++++++++++++++++++++++++ files/shlib/git.sh | 70 +++++++++ 4 files changed, 544 insertions(+) diff --git a/files/hooks/git-snapshot.sh b/files/hooks/git-snapshot.sh new file mode 100644 index 0000000..cc3b2df --- /dev/null +++ b/files/hooks/git-snapshot.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# roverlay hook that maintains (creates) a git history of the overlay +# +# What this script does: +# +# * check whether the git repo exists, else create it +# * check whether a clean commit can be made, that is (a) there are changes +# to commit and (b) the git index does not contain uncommitted changes +# * then, add/commit changes +# +set -u + +## load core functions +. "${FUNCTIONS?}" || exit +#dont_run_as_root + +## load git helper functions +$lf git +#autodie qwhich ${GIT} + +## "config" for this script +# FIXME/TODO: remove config here? +GIT_COMMIT_AUTHOR='undef undef@undef.org' +GIT_COMMIT_MESSAGE='roverlay updates' + + +## other vars +EX_ADD_ERR=2 +EX_COMMIT_ERR=3 + + +## functions + +# void git_try_rollback() +# +# Trap function that tries to reset the git tree/index. +# +git_try_rollback() { + # release trap + trap - INT TERM EXIT + run_command_logged ${GIT} reset --mixed --quiet || true +} + +# int git_create_snapshot() +# +# Adds changes and creates a commit. +# +git_create_snapshot() { + trap git_try_rollback INT TERM EXIT + + # add changes + # --all: add changed, new and deleted files + if ! run_command_logged ${GIT} add --all; then + git_try_rollback + return ${EX_ADD_ERR} + fi + + # commit + # FIXME: + # --author=? + # --file=? or --message=? + if run_command_logged \ + ${GIT} commit --quiet --no-edit \ + --message="${GIT_COMMIT_MESSAGE}" --author="${GIT_COMMIT_AUTHOR}" + then + trap - INT TERM EXIT + return 0 + else + git_try_rollback + return ${EX_COMMIT_ERR} + fi +} + + +## main + +# $GIT_DIR, $S/.git, $HOME/.git, ...? +if [ -d "${S}/.git" ]; then + true +if [ ! -e "${S}/.git" ]; then + einfo "Creating git repo" + # FIXME: --shared OK? + autodie ${GIT} init --quiet --shared=group "${S}" +else + die "'${S}/.git should be a directory." +fi + + +if git_has_changes; then + + autodie git_create_snapshot + + ##push changes to local repo? + ## + ##if ! yesno ${NOSYNC}; then + ## #push changes to remote? + ##fi + +else + veinfo "${SCRIPT_NAME}: nothing to do." + exit 0 +fi diff --git a/files/hooks/mux.sh b/files/hooks/mux.sh new file mode 100755 index 0000000..0764b4d --- /dev/null +++ b/files/hooks/mux.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# simple roverlay hook that runs other hooks (by sourcing them) +# +set -u + +## load core functions +. "${FUNCTIONS?}" || exit +#dont_run_as_root + + +for hookfile in \ + ${FILESDIR}/hooks/${ROVERLAY_PHASE}/?*.sh \ + ${FILESDIR}/hooks/?*.${ROVERLAY_PHASE} +do + if [ -f "${hookfile}" ]; then + #subshell? + #( . "${hookfile}"; ) || ... + + # initial directory should always be $S + + cd "${S}" && . "${hookfile}" || \ + die "errors occured while running hook '${hookfile}'" + fi +done diff --git a/files/shlib/functions.sh b/files/shlib/functions.sh new file mode 100644 index 0000000..d429ff2 --- /dev/null +++ b/files/shlib/functions.sh @@ -0,0 +1,346 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# R overlay -- shell functions +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. +# +# +# Notes: +# * no bashisms here +# +# +# --- functions provided by this file --- +# +# message: +# void veinfo ( message ) +# void einfo ( message ) +# void ewarn ( message ) +# void eerror ( message ) +# +# core: +# @noreturn die ( [message], [exit_code] ), raises exit() +# @noreturn OUT_OF_BOUNDS(), raises die() +# int run_command ( *cmdv ) +# int run_command_logged ( *cmdv ) +# void autodie ( *cmdv ), raises die() +# +# void load_functions ( *filenames, **SHLIB ), raises die() +# void dont_run_as_root(), raises die() +# int list_has ( word, *list_items ) +# int qwhich ( *command ) +# +# fs util: +# int dodir ( *dir ) +# +# str util: +# int yesno ( word, **YESNO_YES=0, **YESNO_NO=1, **YESNO_EMPTY=2 ) +# ~int str_strim ( *args ) +# ~int str_upper ( *args ) +# ~int str_lower ( *args ) +# ~int str_field ( fieldspec, *args, **FIELD_SEPARATOR=' ' ) +# +# int util: +# @intcheck is_int() +# @intcheck is_natural() +# @intcheck is_positive() +# @intcheck is_negative() +# +# +# --- variables provided by this file --- +# +# IFS_DEFAULT +# IFS_NEWLINE +# +# DEVNULL +# +# EX_ERR +# EX_ARG_ERR +# +# SCRIPT_FILENAME +# SCRIPT_NAME +# +# lf +# "reference" to load_functions() +# +# @private __HAVE_CORE_FUNCTIONS__ +# +# --- END HEADER --- + +if [ -z "${__HAVE_CORE_FUNCTIONS__-}" ]; then +readonly __HAVE_CORE_FUNCTIONS__=y + +## make some env vars readonly + +readonly FUNCTIONS +[ -z "${SHLIB-}" ] || readonly SHLIB + +readonly DEBUG VERBOSE QUIET NO_COLOR + +readonly ROVERLAY_PHASE \ + OVERLAY S \ + DISTROOT \ + TMPDIR T \ + ADDITIONS_DIR FILESDIR \ + EBUILD NOSYNC + + +## vars / constants + +readonly IFS_DEFAULT="${IFS}" +readonly IFS_NEWLINE=' +' + +: ${DEVNULL:=/dev/null} +readonly DEVNULL + +readonly EX_ERR=2 +readonly EX_ARG_ERR=5 + +readonly SCRIPT_FILENAME="${0##*/}" +readonly SCRIPT_NAME="${SCRIPT_FILENAME%.*}" + +readonly lf=load_functions + + +## message functions + +# void veinfo ( message ) [**DEBUG] +# +if [ "${DEBUG:?}" = "y" ]; then + veinfo() { echo "$*"; } +else + veinfo() { return 0; } +fi + +# void einfo ( message ) [**VERBOSE] +# +if [ "${VERBOSE:?}" = "y" ]; then + einfo() { echo "$*"; } +else + einfo() { return 0; } +fi + +# void ewarn ( message ) [**QUIET] +# +if [ "${QUIET:?}" != "y" ]; then + ewarn() { echo "$*" 1>&2; } +else + ewarn() { return 0; } +fi + +# void eerror ( message ) +# +eerror() { echo "$*" 1>&2; } + + +## core functions + +# @noreturn die ( [message], [exit_code=**EX_ERR] ), raises exit (exit_code) +# +# Prints a message to stderr and exits afterwards. +# +die() { + if [ -n "${1-}" ]; then + eerror "died: ${1}" + else + eerror "died." + fi + exit "${2:-${EX_ERR?}}" +} + +# @noreturn OUT_OF_BOUNDS(), raises die (**EX_ARG_ERR) +# +# Catches non-zero shift return and calls die(). +# +OUT_OF_BOUNDS() { die "shift returned non-zero." ${EX_ARG_ERR?}; } + +# int run_command ( *cmdv ) +# +# Runs a command and passes its return value. Also logs the command. +# +run_command() { + veinfo "running command: $*" + "$@" +} + +# int run_command_logged ( *cmdv ) +# +# Runs a command and passes its return value. Also logs the command + result. +# +run_command_logged() { + local rc=0 + veinfo "running command: $*" + "$@" || rc=${?} + if [ ${rc} -eq 0 ]; then + veinfo "command succeeded." + return 0 + else + einfo "command '$*' returned ${rc}." + return ${rc} + fi +} + +# void autodie ( *cmdv ), raises die() +# +# Executes a commands. Dies on non-zero return code. +# +autodie() { + local rc=0 + veinfo "running command: $*" + "$@" || rc=$? + if [ ${rc} -eq 0 ]; then + return 0 + else + die "command '$*' returned ${rc}" ${rc} + fi +} + +# void load_functions ( *filenames, **SHLIB ), raises die() +# +# Loads zero or more additional shell function files from $SHLIB. +# Dies if a file cannot be sourced. +# +load_functions() { + [ -n "${SHLIB-}" ] || die "\$SHLIB is not set." + local f + while [ $# -gt 0 ]; do + f="${SHLIB}/${1%.sh}.sh" + veinfo "Trying to load functions file ${f} ... " + . "${f}" || die "failed to load functions file ${f}." + shift + done + return 0 +} + +# void dont_run_as_root(), raises die() +# +# Dies if this process is run as root. +# +dont_run_as_root() { + local uid=$(id -ru) + if [ -z "${uid}" ]; then + die "cannot get \$uid." + elif [ ${uid} -ne 0 2>>${DEVNULL} ]; then + return 0 + else + die "bad \$uid ${uid}." + fi +} + +# int list_has ( word, *list_items ) +# +# Returns true if word is in list_items, else false. +# +list_has() { + local kw="${1}" + shift || OUT_OF_BOUNDS + + while [ $# -gt 0 ]; do + [ "x${kw}" != "x${1}" ] || return 0 + shift + done + return 1 +} + +# int qwhich ( *command ) +# +# Returns true if 'which' finds all listed commands, else false. +# +qwhich() { + while [ $# -gt 0 ]; do + which "${1}" 1>>${DEVNULL} 2>>${DEVNULL} || return 1 + shift + done + return 0 +} + +## fs util functions + +# int dodir ( *dir ) +# +# Ensures that the given directories exist by creating them if necessary. +# +# Returns the number of directories that could not be created. +# +dodir() { + local fail=0 + while [ $# -gt 0 ]; do + [ -d "${1}" ] || mkdir -p -- "${1}" || fail=$(( ${fail} + 1 )) + done + return ${fail} +} + + +## str util functions + +# int yesno ( word, **YESNO_YES=0, **YESNO_NO=1, **YESNO_EMPTY=2 ) +# +# Returns: +# * YESNO_YES (0) if word means yes +# * YESNO_EMPTY (2) if word is empty +# * YESNO_NO (1) otherwise (word is not empty and does not mean yes) +# +yesno() { + case "${1-}" in + '') + return ${YESNO_EMPTY:-2} + ;; + # yes | y | true | 1 | enable(d) | on + [yY][eE][sS]|\ + [yY]|\ + [tT][rR][uU][eE]|\ + 1|\ + [eE][nN][aA][bB][lL][eE]?|\ + [oO][nN]\ + ) + return ${YESNO_YES:-0} + ;; + *) + return ${YESNO_NO:-1} + ;; + esac +} + +# ~int str_strim ( *args ) +# +# Removes whitespace at the beginning + end of a string +# and replaces any whitespace sequence within the string +# with a single space char. +# +str_trim() { sed -r -e 's,^\s+,,' -e 's,\s+$,,' -e 's,\s+, ,g' "$@"; } + +# ~int str_upper ( *args ) +str_upper() { tr [:lower:] [:upper:] "$@"; } + +# ~int str_lower ( *args ) +str_lower() { tr [:upper:] [:lower:] "$@"; } + +# ~int str_field ( fieldspec, *args, **FIELD_SEPARATOR=' ' ) +# +str_field() { cut -d "${FIELD_SEPARATOR:- }" -f "$@"; } + + +## int util functions + +# @funcdef shbool @intcheck [:=true] ( word ) +# +# Returns true if word is a number and condition(word) evaluates to true. +# + +# @intcheck is_int() +is_int() { + [ -n "${1-}" ] || return 1 + [ "${1}" -ge 0 2>>${DEVNULL} ] || [ "${1}" -lt 0 2>>${DEVNULL} ] +} + +# @intcheck >=0 is_natural() +is_natural() { [ -n "${1-}" ] && [ "${1}" -ge 0 2>>${DEVNULL} ]; } + +# @intcheck >0 is_positive() +is_positive() { [ -n "${1-}" ] && [ "${1}" -gt 0 2>>${DEVNULL} ]; } + +# @intcheck <0 is_negative() +is_negative() { [ -n "${1-}" ] && [ "${1}" -lt 0 2>>${DEVNULL} ]; } + +fi diff --git a/files/shlib/git.sh b/files/shlib/git.sh new file mode 100644 index 0000000..89c78ce --- /dev/null +++ b/files/shlib/git.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# R overlay -- shell functions, git +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. +# +# +# --- functions provided by this file --- +# +# int git_has_changes ( [*files] ), raises die() +# +# +# --- variables provided by this file --- +# +# GIT +# @private __GIT_DIFF_OPTS +# +# @private __HAVE_GIT_FUNCTIONS__ +# +# +# --- END HEADER --- + +if [ -z "${__HAVE_GIT_FUNCTIONS__-}" ]; then +readonly __HAVE_GIT_FUNCTIONS__=y + +if [ -z "${GIT-}" ]; then + GIT=$(which git 2>>${DEVNULL?}) + : ${GIT:=git} +fi + +: ${__GIT_DIFF_OPTS=--no-ext-diff --quiet --ignore-submodules} + + +# int git_has_changes ( [*files] ), raises die() +# +# inspired by git-sh-setup.sh, require_clean_work_tree() from the git source +# +# Checks whether >the< git repo has unstaged changes. Also checks whether it +# has any uncommitted changes and dies if there are any (i.e., no clean commit +# possible). +# +# Returns 0 if there's anything to commit, else 1. +# +git_has_changes() { + ${GIT} rev-parse --quiet --verify HEAD 1>>${DEVNULL} || \ + die "git rev-parse returned ${?}." ${?} + #FIXME: return code if update-index? + run_command_logged \ + ${GIT} update-index -q --ignore-submodules --refresh -- "$@" + + local has_changes + if ${GIT} diff-files ${__GIT_DIFF_OPTS} "$@"; then + ## return value of zero means no changes + veinfo "git index: no changes found" + has_changes=1 + else + veinfo "git index: changes found" + has_changes=0 + fi + + if ${GIT} diff-index --cached ${__GIT_DIFF_OPTS} HEAD -- "$@"; then + veinfo "git index: no uncommitted changes found (that's good)" + else + die "uncommitted changes in git index found." + fi + return ${has_changes} +} + +fi From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id 4C32F1381F3 for ; Fri, 21 Jun 2013 14:09:36 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 1FAABE0983; Fri, 21 Jun 2013 14:09:34 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 9B845E0983 for ; Fri, 21 Jun 2013 14:09:33 +0000 (UTC) Received: from hornbill.gentoo.org (hornbill.gentoo.org [94.100.119.163]) (using TLSv1 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 43EB233E6A6 for ; Fri, 21 Jun 2013 14:09:32 +0000 (UTC) Received: from localhost.localdomain (localhost [127.0.0.1]) by hornbill.gentoo.org (Postfix) with ESMTP id CE20CE468F for ; Fri, 21 Jun 2013 14:09:30 +0000 (UTC) From: "André Erdmann" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "André Erdmann" Message-ID: <1371823644.f814f32ab17ace769f25ccf80ca77d93a82c3a92.dywi@gentoo> Subject: [gentoo-commits] proj/R_overlay:gsoc13/next commit in: files/shlib/, files/hooks/ X-VCS-Repository: proj/R_overlay X-VCS-Files: files/hooks/git-snapshot.sh files/hooks/mux.sh files/shlib/functions.sh files/shlib/git.sh X-VCS-Directories: files/shlib/ files/hooks/ X-VCS-Committer: dywi X-VCS-Committer-Name: André Erdmann X-VCS-Revision: f814f32ab17ace769f25ccf80ca77d93a82c3a92 X-VCS-Branch: gsoc13/next Date: Fri, 21 Jun 2013 14:09:30 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Archives-Salt: e0417728-31c1-4ac9-9bf9-71d6ca4b31a1 X-Archives-Hash: 7e7df7e08a54eba5aa43de36112c3625 Message-ID: <20130621140930.F9sdHcPY112d7Lr5GpKupqQkz5pnJZmppn6z4PLtz7c@z> commit: f814f32ab17ace769f25ccf80ca77d93a82c3a92 Author: André Erdmann mailerd de> AuthorDate: Fri Jun 21 14:07:24 2013 +0000 Commit: André Erdmann mailerd de> CommitDate: Fri Jun 21 14:07:24 2013 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=f814f32a roverlay/files: shlib, hooks This commit adds shell script files for running hooks. shlib/ contains function files and hooks/ contains the actual scripts. --- files/hooks/git-snapshot.sh | 103 +++++++++++++ files/hooks/mux.sh | 25 ++++ files/shlib/functions.sh | 346 ++++++++++++++++++++++++++++++++++++++++++++ files/shlib/git.sh | 70 +++++++++ 4 files changed, 544 insertions(+) diff --git a/files/hooks/git-snapshot.sh b/files/hooks/git-snapshot.sh new file mode 100644 index 0000000..cc3b2df --- /dev/null +++ b/files/hooks/git-snapshot.sh @@ -0,0 +1,103 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# roverlay hook that maintains (creates) a git history of the overlay +# +# What this script does: +# +# * check whether the git repo exists, else create it +# * check whether a clean commit can be made, that is (a) there are changes +# to commit and (b) the git index does not contain uncommitted changes +# * then, add/commit changes +# +set -u + +## load core functions +. "${FUNCTIONS?}" || exit +#dont_run_as_root + +## load git helper functions +$lf git +#autodie qwhich ${GIT} + +## "config" for this script +# FIXME/TODO: remove config here? +GIT_COMMIT_AUTHOR='undef undef@undef.org' +GIT_COMMIT_MESSAGE='roverlay updates' + + +## other vars +EX_ADD_ERR=2 +EX_COMMIT_ERR=3 + + +## functions + +# void git_try_rollback() +# +# Trap function that tries to reset the git tree/index. +# +git_try_rollback() { + # release trap + trap - INT TERM EXIT + run_command_logged ${GIT} reset --mixed --quiet || true +} + +# int git_create_snapshot() +# +# Adds changes and creates a commit. +# +git_create_snapshot() { + trap git_try_rollback INT TERM EXIT + + # add changes + # --all: add changed, new and deleted files + if ! run_command_logged ${GIT} add --all; then + git_try_rollback + return ${EX_ADD_ERR} + fi + + # commit + # FIXME: + # --author=? + # --file=? or --message=? + if run_command_logged \ + ${GIT} commit --quiet --no-edit \ + --message="${GIT_COMMIT_MESSAGE}" --author="${GIT_COMMIT_AUTHOR}" + then + trap - INT TERM EXIT + return 0 + else + git_try_rollback + return ${EX_COMMIT_ERR} + fi +} + + +## main + +# $GIT_DIR, $S/.git, $HOME/.git, ...? +if [ -d "${S}/.git" ]; then + true +if [ ! -e "${S}/.git" ]; then + einfo "Creating git repo" + # FIXME: --shared OK? + autodie ${GIT} init --quiet --shared=group "${S}" +else + die "'${S}/.git should be a directory." +fi + + +if git_has_changes; then + + autodie git_create_snapshot + + ##push changes to local repo? + ## + ##if ! yesno ${NOSYNC}; then + ## #push changes to remote? + ##fi + +else + veinfo "${SCRIPT_NAME}: nothing to do." + exit 0 +fi diff --git a/files/hooks/mux.sh b/files/hooks/mux.sh new file mode 100755 index 0000000..0764b4d --- /dev/null +++ b/files/hooks/mux.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# simple roverlay hook that runs other hooks (by sourcing them) +# +set -u + +## load core functions +. "${FUNCTIONS?}" || exit +#dont_run_as_root + + +for hookfile in \ + ${FILESDIR}/hooks/${ROVERLAY_PHASE}/?*.sh \ + ${FILESDIR}/hooks/?*.${ROVERLAY_PHASE} +do + if [ -f "${hookfile}" ]; then + #subshell? + #( . "${hookfile}"; ) || ... + + # initial directory should always be $S + + cd "${S}" && . "${hookfile}" || \ + die "errors occured while running hook '${hookfile}'" + fi +done diff --git a/files/shlib/functions.sh b/files/shlib/functions.sh new file mode 100644 index 0000000..d429ff2 --- /dev/null +++ b/files/shlib/functions.sh @@ -0,0 +1,346 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# R overlay -- shell functions +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. +# +# +# Notes: +# * no bashisms here +# +# +# --- functions provided by this file --- +# +# message: +# void veinfo ( message ) +# void einfo ( message ) +# void ewarn ( message ) +# void eerror ( message ) +# +# core: +# @noreturn die ( [message], [exit_code] ), raises exit() +# @noreturn OUT_OF_BOUNDS(), raises die() +# int run_command ( *cmdv ) +# int run_command_logged ( *cmdv ) +# void autodie ( *cmdv ), raises die() +# +# void load_functions ( *filenames, **SHLIB ), raises die() +# void dont_run_as_root(), raises die() +# int list_has ( word, *list_items ) +# int qwhich ( *command ) +# +# fs util: +# int dodir ( *dir ) +# +# str util: +# int yesno ( word, **YESNO_YES=0, **YESNO_NO=1, **YESNO_EMPTY=2 ) +# ~int str_strim ( *args ) +# ~int str_upper ( *args ) +# ~int str_lower ( *args ) +# ~int str_field ( fieldspec, *args, **FIELD_SEPARATOR=' ' ) +# +# int util: +# @intcheck is_int() +# @intcheck is_natural() +# @intcheck is_positive() +# @intcheck is_negative() +# +# +# --- variables provided by this file --- +# +# IFS_DEFAULT +# IFS_NEWLINE +# +# DEVNULL +# +# EX_ERR +# EX_ARG_ERR +# +# SCRIPT_FILENAME +# SCRIPT_NAME +# +# lf +# "reference" to load_functions() +# +# @private __HAVE_CORE_FUNCTIONS__ +# +# --- END HEADER --- + +if [ -z "${__HAVE_CORE_FUNCTIONS__-}" ]; then +readonly __HAVE_CORE_FUNCTIONS__=y + +## make some env vars readonly + +readonly FUNCTIONS +[ -z "${SHLIB-}" ] || readonly SHLIB + +readonly DEBUG VERBOSE QUIET NO_COLOR + +readonly ROVERLAY_PHASE \ + OVERLAY S \ + DISTROOT \ + TMPDIR T \ + ADDITIONS_DIR FILESDIR \ + EBUILD NOSYNC + + +## vars / constants + +readonly IFS_DEFAULT="${IFS}" +readonly IFS_NEWLINE=' +' + +: ${DEVNULL:=/dev/null} +readonly DEVNULL + +readonly EX_ERR=2 +readonly EX_ARG_ERR=5 + +readonly SCRIPT_FILENAME="${0##*/}" +readonly SCRIPT_NAME="${SCRIPT_FILENAME%.*}" + +readonly lf=load_functions + + +## message functions + +# void veinfo ( message ) [**DEBUG] +# +if [ "${DEBUG:?}" = "y" ]; then + veinfo() { echo "$*"; } +else + veinfo() { return 0; } +fi + +# void einfo ( message ) [**VERBOSE] +# +if [ "${VERBOSE:?}" = "y" ]; then + einfo() { echo "$*"; } +else + einfo() { return 0; } +fi + +# void ewarn ( message ) [**QUIET] +# +if [ "${QUIET:?}" != "y" ]; then + ewarn() { echo "$*" 1>&2; } +else + ewarn() { return 0; } +fi + +# void eerror ( message ) +# +eerror() { echo "$*" 1>&2; } + + +## core functions + +# @noreturn die ( [message], [exit_code=**EX_ERR] ), raises exit (exit_code) +# +# Prints a message to stderr and exits afterwards. +# +die() { + if [ -n "${1-}" ]; then + eerror "died: ${1}" + else + eerror "died." + fi + exit "${2:-${EX_ERR?}}" +} + +# @noreturn OUT_OF_BOUNDS(), raises die (**EX_ARG_ERR) +# +# Catches non-zero shift return and calls die(). +# +OUT_OF_BOUNDS() { die "shift returned non-zero." ${EX_ARG_ERR?}; } + +# int run_command ( *cmdv ) +# +# Runs a command and passes its return value. Also logs the command. +# +run_command() { + veinfo "running command: $*" + "$@" +} + +# int run_command_logged ( *cmdv ) +# +# Runs a command and passes its return value. Also logs the command + result. +# +run_command_logged() { + local rc=0 + veinfo "running command: $*" + "$@" || rc=${?} + if [ ${rc} -eq 0 ]; then + veinfo "command succeeded." + return 0 + else + einfo "command '$*' returned ${rc}." + return ${rc} + fi +} + +# void autodie ( *cmdv ), raises die() +# +# Executes a commands. Dies on non-zero return code. +# +autodie() { + local rc=0 + veinfo "running command: $*" + "$@" || rc=$? + if [ ${rc} -eq 0 ]; then + return 0 + else + die "command '$*' returned ${rc}" ${rc} + fi +} + +# void load_functions ( *filenames, **SHLIB ), raises die() +# +# Loads zero or more additional shell function files from $SHLIB. +# Dies if a file cannot be sourced. +# +load_functions() { + [ -n "${SHLIB-}" ] || die "\$SHLIB is not set." + local f + while [ $# -gt 0 ]; do + f="${SHLIB}/${1%.sh}.sh" + veinfo "Trying to load functions file ${f} ... " + . "${f}" || die "failed to load functions file ${f}." + shift + done + return 0 +} + +# void dont_run_as_root(), raises die() +# +# Dies if this process is run as root. +# +dont_run_as_root() { + local uid=$(id -ru) + if [ -z "${uid}" ]; then + die "cannot get \$uid." + elif [ ${uid} -ne 0 2>>${DEVNULL} ]; then + return 0 + else + die "bad \$uid ${uid}." + fi +} + +# int list_has ( word, *list_items ) +# +# Returns true if word is in list_items, else false. +# +list_has() { + local kw="${1}" + shift || OUT_OF_BOUNDS + + while [ $# -gt 0 ]; do + [ "x${kw}" != "x${1}" ] || return 0 + shift + done + return 1 +} + +# int qwhich ( *command ) +# +# Returns true if 'which' finds all listed commands, else false. +# +qwhich() { + while [ $# -gt 0 ]; do + which "${1}" 1>>${DEVNULL} 2>>${DEVNULL} || return 1 + shift + done + return 0 +} + +## fs util functions + +# int dodir ( *dir ) +# +# Ensures that the given directories exist by creating them if necessary. +# +# Returns the number of directories that could not be created. +# +dodir() { + local fail=0 + while [ $# -gt 0 ]; do + [ -d "${1}" ] || mkdir -p -- "${1}" || fail=$(( ${fail} + 1 )) + done + return ${fail} +} + + +## str util functions + +# int yesno ( word, **YESNO_YES=0, **YESNO_NO=1, **YESNO_EMPTY=2 ) +# +# Returns: +# * YESNO_YES (0) if word means yes +# * YESNO_EMPTY (2) if word is empty +# * YESNO_NO (1) otherwise (word is not empty and does not mean yes) +# +yesno() { + case "${1-}" in + '') + return ${YESNO_EMPTY:-2} + ;; + # yes | y | true | 1 | enable(d) | on + [yY][eE][sS]|\ + [yY]|\ + [tT][rR][uU][eE]|\ + 1|\ + [eE][nN][aA][bB][lL][eE]?|\ + [oO][nN]\ + ) + return ${YESNO_YES:-0} + ;; + *) + return ${YESNO_NO:-1} + ;; + esac +} + +# ~int str_strim ( *args ) +# +# Removes whitespace at the beginning + end of a string +# and replaces any whitespace sequence within the string +# with a single space char. +# +str_trim() { sed -r -e 's,^\s+,,' -e 's,\s+$,,' -e 's,\s+, ,g' "$@"; } + +# ~int str_upper ( *args ) +str_upper() { tr [:lower:] [:upper:] "$@"; } + +# ~int str_lower ( *args ) +str_lower() { tr [:upper:] [:lower:] "$@"; } + +# ~int str_field ( fieldspec, *args, **FIELD_SEPARATOR=' ' ) +# +str_field() { cut -d "${FIELD_SEPARATOR:- }" -f "$@"; } + + +## int util functions + +# @funcdef shbool @intcheck [:=true] ( word ) +# +# Returns true if word is a number and condition(word) evaluates to true. +# + +# @intcheck is_int() +is_int() { + [ -n "${1-}" ] || return 1 + [ "${1}" -ge 0 2>>${DEVNULL} ] || [ "${1}" -lt 0 2>>${DEVNULL} ] +} + +# @intcheck >=0 is_natural() +is_natural() { [ -n "${1-}" ] && [ "${1}" -ge 0 2>>${DEVNULL} ]; } + +# @intcheck >0 is_positive() +is_positive() { [ -n "${1-}" ] && [ "${1}" -gt 0 2>>${DEVNULL} ]; } + +# @intcheck <0 is_negative() +is_negative() { [ -n "${1-}" ] && [ "${1}" -lt 0 2>>${DEVNULL} ]; } + +fi diff --git a/files/shlib/git.sh b/files/shlib/git.sh new file mode 100644 index 0000000..89c78ce --- /dev/null +++ b/files/shlib/git.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# R overlay -- shell functions, git +# Copyright (C) 2013 André Erdmann +# Distributed under the terms of the GNU General Public License; +# either version 2 of the License, or (at your option) any later version. +# +# +# --- functions provided by this file --- +# +# int git_has_changes ( [*files] ), raises die() +# +# +# --- variables provided by this file --- +# +# GIT +# @private __GIT_DIFF_OPTS +# +# @private __HAVE_GIT_FUNCTIONS__ +# +# +# --- END HEADER --- + +if [ -z "${__HAVE_GIT_FUNCTIONS__-}" ]; then +readonly __HAVE_GIT_FUNCTIONS__=y + +if [ -z "${GIT-}" ]; then + GIT=$(which git 2>>${DEVNULL?}) + : ${GIT:=git} +fi + +: ${__GIT_DIFF_OPTS=--no-ext-diff --quiet --ignore-submodules} + + +# int git_has_changes ( [*files] ), raises die() +# +# inspired by git-sh-setup.sh, require_clean_work_tree() from the git source +# +# Checks whether >the< git repo has unstaged changes. Also checks whether it +# has any uncommitted changes and dies if there are any (i.e., no clean commit +# possible). +# +# Returns 0 if there's anything to commit, else 1. +# +git_has_changes() { + ${GIT} rev-parse --quiet --verify HEAD 1>>${DEVNULL} || \ + die "git rev-parse returned ${?}." ${?} + #FIXME: return code if update-index? + run_command_logged \ + ${GIT} update-index -q --ignore-submodules --refresh -- "$@" + + local has_changes + if ${GIT} diff-files ${__GIT_DIFF_OPTS} "$@"; then + ## return value of zero means no changes + veinfo "git index: no changes found" + has_changes=1 + else + veinfo "git index: changes found" + has_changes=0 + fi + + if ${GIT} diff-index --cached ${__GIT_DIFF_OPTS} HEAD -- "$@"; then + veinfo "git index: no uncommitted changes found (that's good)" + else + die "uncommitted changes in git index found." + fi + return ${has_changes} +} + +fi