diff options
77 files changed, 3158 insertions, 1218 deletions
diff --git a/HACKING.md b/HACKING.md new file mode 100644 index 0000000..ebe595b --- /dev/null +++ b/HACKING.md @@ -0,0 +1,223 @@ +This document is a little all over the place--I've been working on it +for a while, but I keep refactoring it as I move pieces of it into man +pages and whatnot. It's mostly a brain dump, sorry. + +There are three parts to this; procedures, "content" guidelines and +"style" guidelines. The style guidelines are less strict; as long as +things are consistent at the file-level, I'm pretty happy. + +Contributing +============ + +I'd love to have your patches! Code should be hackable; if you want +to modify something, but can't figure out how: 1) ping me for help, 2) +it probably means the code was too complicated in the first place. + +Patches should be sent to <dev@lists.parabolagnulinux.org>; please put +"[PATCH]" and "libretools" in the subject line. If you have commit +access, but want me to look over it first, feel free to create a new +branch in git, and I will notice it. Try to avoid pushing to the +"master" branch unless it's a trivial change; it makes it easier to +review things; though I *will* look over every commit before I do a +release, so don't think you can sneak something in :) + +I'd love to discuss possible changes on IRC (I'm lukeshu), either on +irc.freenode.net#parabola or in personal messages. My account may be +online even if I'm not; I will eventually see your it, I do a search +for mentions of "luke" on #parabola every time I get on. + +Code content +============ + +Be aware of the `librelib(7)` library suite, which lives `src/lib`. +It is a suite of Bash libraries that will help you out. Most of the +people looking at the libretools code are familiar with the `messages` +part of it, which actually contains a much of utility routines, not +just message printing. There is also a library for dealing with +`blacklist.txt`, and one for loading configuration files and +PKGBUILDs. These are common tasks, but are tricky to handle +consistently--the libraries are there to make things easier. Take a +look at the man pages. + +Message printing: All of the message printing routines, except for +`term_title` and `flag`, take printf-type arguments. Take advantage +of that; don't use string interpolation (don't do `"foo ${var} +bar"`). The reason for this is that if you don't do string +interpolation, messages can be automatically internationalized. +(Internationalization is incomplete at the momement) + +Message printing: The in `--help`/`-h` text, use `print` to print +lines that should not wrap, `echo` to print blank lines, `prose` to +print paragraphs, `bullet` to print bullet points, and `flag` to print +option flags. The text should follow this general format: + + print "Usage: %s [OPTIONS] VARS_ARE_UNDERSCORE_AND_CAPITAL" "${program_name}" + print "One line description of program, no period" + echo + prose "More details. This is a paragraph." + echo + print "Options:" + flag "-h" "Show this message" + +In the "Usage:" line, use printf `%s` and the value `"${0##*/}"` to +determine the program name at runtime. + +There used to be guidelines for how to align the option flags and +descriptions, but now the `flag` command exists takes care of it for +you. Yay for things being easier! + +When using `set -u`, `set -e`, or `trap`, you should also use `set -E` +to have the error handling be passed down to subshells. + +Feel free to use `set -e` (fail on error), but be careful of the +caveats (there are a bunch of them); don't assume all errors are +checked because of it. + +Use `set -u` if you can; it makes using an unset variable an error. + - If a variable not being set is valid (perhaps a configuration + option), use `${var:-}` when accessing it to suppress the error. + - An empty array counts as unset, so if you have an array that may be + empty, use `set +u` before accessing it. + - The reason for this is that a normal string variable is basically + an array with length=1; an unset variable looks like an array + with length=0. Weird stuff. + +In the shebang, use `#!/usr/bin/env bash`. This allows us to not +hardcode the location of bash (I'm not sure why this is useful for +something distro-dependent like libretools, but fauno seems to have a +use-case for it). + +In the shebang, don't pass flags to bash, besides breaking `env` +(above), it means people will make mistakes when debugging, and +running things with `bash FILENAME`. Instead, use `set` to adjust the +flags inside of the program. + +Obey `$TMPDIR`. It's usually as easy as passing `--tmpdir` to +`mktemp`. + +Use `trap` to clean up your temporary files. This way, even if your +program terminates early, things will be cleaned up. + +Bash best practices +=================== + +Basically, know what you are doing, and be safe with it. The problem +is that most people don't know about safe bash scripting. + +A lot of people look at the "Advanced Bash Scripting" ebook--DO NOT do +that, it is trash... though it contains a "reference card" page that +may be useful and isn't trash. + +Take a look at Gentoo's Bash guidelines +<http://devmanual.gentoo.org/tools-reference/bash/index.html>. +They're pretty good, and cover most of the "gotcha's" about Bash +syntax. It mentions but discourages the use of Bash 3 +features... why? Who still uses Bash 2? Feel free to use Bash 4 +features! + +I wrote an article on Bash arrays +<https://lukeshu.com/blog/bash-arrays.html>. A lot of people think +they're tricky, but they're simple once you know how they work. It's +short enough that you should read the whole thing. Know the +difference between `"${array[@]}"` and `"${array[*]}"`. And I'll say +it again here, ALWAYS wrap those in double quotes; there is no reason +I can think of that the unquoted behavior would ever be the correct +thing. + +My brief rules of thumb: + + - Quote every variable. + - That includes arrays: `"${array[@]}"` and `"${array[*]}"`. + - In most (but not all!) cases inside of `[[ ... ]]` conditions, + variables don't need to be quoted. When in doubt, quote them. + - When assigning one variable to another, you don't need quotes; + you don't need quotes for `foo=$bar` + - Try to avoid global variables; declare all variables in functions + with `local`. + - Or `declare`; inside of a function, unless you pass the `-g` + flag, `declare` makes the variable local. + - Use `local VAR` before a `for VAR in LIST` loop--the variable is created in the + current scope, not the scope of the loop. + - Feeding input to `while` loops is weird because of how subshells + work: + + # Input from a file + # BAD + cat file | while read line; do + ... + done + # GOOD + while read line; do + ... + done <file + + # Input from a program + # BAD + prog | while read line; do + ... + done + # GOOD + while read line; do + ... + done < <(prog) + + +Style guidelines +================ + +Unless you have a good reason, use `[[ ... ]]` instead of `[ ... ]`; +they work similarly, but `[[ ... ]]` is sometimes more readable (fine, +rarely, but never less), and is harder to make mistakes with quoting, +because it is syntactic magic, as opposed to `[ ... ]` which is an +executable which just happens to be implemented as a builtin. + +Use a litteral tab for indent. When indenting line-wrapped text, such +as that for `prose`, do it like this: (» indicates tab, · indicates +space) + + func() { + » prose "This is the first line. This paragraph is going to be + » ·······wrapped." + } + +The `; then` and `; do` should go on the same line as +`if`/`elif`/`for`/`while`. Also, there is no space before the `;`. + +Prefer the `for VAR in LIST` syntax over the `for ((init; cond; inc))` +syntax, when possible. For example (heh, `for` example): + + local i + for (( i = 1 ; i <= 10 ; i++ )); do + +should be + + local i + for i in {1..10}; do + +Of course, if the upper bound is a variable, the C-like syntax is +the better option, as otherwise you would have to use `seq` (calling +an external), or `eval` (gross, easy to mess up royally). + +Indent comments like you would code; don't leave them at the beginning +of the line. Example: + + for item in "${list[@]}"; do + if [[ $item == foo ]]; then + # BAD + foobar + fi + if [[ $item == bar ]]; then + # GOOD + barbaz + fi + done + +Fauno, I'm sorry. But I don't know how you can read your own code :P. + +Some people argue in favor of the useless use of cat, because data +should flow from left to right. However, the input redirection +doesn't have to go on the right side of a command: + + cat file | program # useless use of cat + program < file # data flows right to left + < file program # just right @@ -0,0 +1,119 @@ +Installation of libretools is pretty straight-forward. The only +tricky thing is the weird dependence on the devtools-par source code. + +The build system will by default look at `$(topdir)/../devtools-par` +for the devtools-par source code. This can be changed by adjusting +the `devtoolsdir` configuration variable, see "Configuration" below. + +Once you have the devtools source taken care of, you can have the +default everything-included install by running: + + $ make + # make install + +Dependencies +------------ + +Unlike a lot of software, run-time dependencies are not required at +build-time. + +Libretools is mostly shell scripts. It very specifically targets +Parabola GNU/Linux-libre; there are a lot of dependencies, most of +which are undocumented. Switching away from a GNU user-land is bound +to cause issues. + +## Build dependencies + +The "unusual" build-time dependencies are: + + - GNU Make -- other `make`s will not work. + - GNU sed -- must support `-r` for ERE; BSD sed uses `-E` for this purpose. + - Emacs -- `emacs --batch` is used use Emacs Lisp to process some text. + - ronn -- A markdown-to-manpage converter + +At this time, the build system dos not support not building the +documentation; ronn is required. + +Additionally, other usual "core utilities" are required: + + cat, chmod, cp, echo, false, find, install, ln, msguniq, rm, tr, + xgettext + +Also, `/bin/sh` must support `{brace,expansion}`. If it doesn't, find +a shell that does, and configure `make` to use it. Though, because +libretools is mostly shell scripts, if you deviate too much from a GNU +command-line environment, I suspect that you will run into more +issues. If I were targeting anything other than Parabola, this would +be a bigger issue. + +## Test suite dependencies + +If you wish to run the test suite, you will need the "roundup" shell +unit testing program. On Parabola GNU/Linux-libre it is called +"sh-roundup". If your operating system doesn't have it, it is +available at <http://bmizerany.github.io/roundup/>. + +## Run-time dependencies + +Being mostly shell scripts, many external program are used. Anything +that is included when installing the `base` package group on Parabola +GNU/Linux, I consider an implicit dependency. If something isn't used +now, that doesn't mean it won't be in the future. + +On top of that, the following dependencies are also needed: + +librelib subpackage: + - wget +gitget subpackage: + - librelib (provided) + - git +main libretools subpackage: + - librelib (provided) + - gitget (provided, only needed for `createworkdir`) + - arch-install-scripts + - ssh client (OpenSSH, only needed for `librerelease` + - rsync + - subversion + - tokyocabinet +libretools-mips64el subpackage: + - libretools (provided) + - git + +Configuration +------------- + +This is not a GNU package, there is no `./configure` script. + +There are two ways to set configuration variables: + 1. Edit config.mk + 2. Pass `VARIABLE=VALUE` to `make` + +The configuration variables mostly match GNU packages, but default +values differ; libretools installs to `prefix=/usr` by default instead +of GNU's `prefix=/usr/local`. + +Building and installing subpackages +----------------------------------- + +There are several subpackages you can build and install. This is done +by running: + + $ make build-${package} + # make install-${package} + +respectively. In addition to `build` and `install`, the activities +you can do are: + + - `copy` -- copy necessary files from the devtools-par source code + - `build` -- build all programs and files + - `install` -- install everything + - `clean` -- remove generated files + - `pot` -- generate gettext portable object template files. + +The subpackages you can run these on are: + + - doc -- The non-manpage documentation + - libretools -- The main libretools package + - libretools-mips64el -- Programs useful for porting to mips64el + - librelib -- generic libraries included + - gitget -- A git downloader @@ -4,7 +4,7 @@ include config.mk ################################################################################ # these are the resulting packages -packages=doc libretools libretools-mips64el librelib libregit +packages=doc libretools libretools-mips64el librelib gitget # and which directories they contain doc=doc libretools=\ @@ -17,7 +17,7 @@ libretools=\ src/toru libretools-mips64el=src/mips64el-tools librelib=src/lib -libregit=src/libregit +gitget=src/gitget ################################################################################ @@ -26,23 +26,25 @@ copy: PHONY $(addprefix copy-, $(packages)) build: PHONY $(addprefix build-, $(packages)) install: PHONY $(addprefix install-,$(packages)) clean: PHONY $(addprefix clean-, $(packages)) +pot: PHONY $(addprefix pot-, $(filter-out doc,$(packages))) check: @cd test && ./testenv $(TESTENVFLAGS) roundup -%/copy: PHONY % - $(MAKE) -C $* copy %/build: PHONY % $(MAKE) -C $* %/install: PHONY % $(MAKE) -C $* install %/clean: PHONY % $(MAKE) -C $* clean +%/pot: PHONY % + $(MAKE) -C $* pot .SECONDEXPANSION: $(addprefix copy-, $(packages)): copy-%: PHONY $$(addsuffix /copy, $$($$*)) $(addprefix build-, $(packages)): build-%: PHONY $$(addsuffix /build, $$($$*)) $(addprefix install-,$(packages)): install-%: PHONY $$(addsuffix /install,$$($$*)) $(addprefix clean-, $(packages)): clean-%: PHONY $$(addsuffix /clean, $$($$*)) +$(addprefix pot-, $(packages)): pot-%: PHONY $$(addsuffix /pot, $$($$*)) ################################################################################ @@ -14,22 +14,16 @@ edit = sed -e 's|m4_include(lib/\(.*\))|. $$(librelib \1)|' # Normalize a .po(t) file pofmt = msguniq -Fi +xgettext-keywords-sh += --keyword={_,print} +xgettext-keywords-sh += --keyword={msg,msg2,warning,error,stat_busy,die} +xgettext-keywords-sh += --keyword={lock,slock}:3 + +xgettext-sh = xgettext --omit-header --from-code=UTF-8 -L shell -o - +xgettext-sh-std = $(xgettext-sh) $(xgettext-keywords-sh) $^ + pot-remove-wrap = tr '\n' '\r' | sed 's/"\r\s*"//g' | tr '\r' '\n' html-whitespace-collapse = sed -r -e 's/(\\n|\\t|\t)/ /g' -e 's/ +/ /g' -define xgettext-prose = - xgettext --omit-header --from-code=UTF-8 -L shell \ - -k \ - --keyword={prose,bullet,flag:2} \ - -o - $^ | $(pot-remove-wrap) | $(html-whitespace-collapse) | sed '/^#, sh-format/d' -endef -define xgettext-main = - xgettext --omit-header --from-code=UTF-8 -L shell \ - -k \ - --keyword={_,print} \ - --keyword={msg,msg2,warning,error,stat_busy} \ - --keyword={lock,slock}:3 \ - -o - $^ -endef +xgettext-sh-prose = $(xgettext-sh) -k --keyword={prose,bullet,flag:2} $^ | $(pot-remove-wrap) | $(html-whitespace-collapse) | sed '/^\#, sh-format/d' # Usage: as a variable # relative path to `/` from $(bindir) @@ -61,6 +55,9 @@ install_files += $(addprefix $(DESTDIR)$(bindir)/,$(progs)) \ $(addprefix $(DESTDIR)$(mandir)/man8/,$(filter %.8,$(mans))) clean_files += $(patsubst %.in,%,$(copy_files)) $(copy_files) $(mans) $(wildcard *.pot) +pots += $(filter-out $(no-pots) ,$(progs)) +pot_files += $(addsuffix .pot,$(pots)) + # Set the default target ####################################################### all: PHONY build @@ -84,11 +81,18 @@ build: PHONY $(build_files) ronn --html $(RONNFLAGS) < '$<' > '$@' %: %.in @echo "GEN $@" - @$(edit) <"$<" >"$@" || { rm -f -- '$@'; false; } - @chmod 755 "$@" || { rm -f -- '$@'; false; } + @$(edit) <"$<" >"$@" + @chmod 755 "$@" + +# Build ######################################################################## + +pot: everything.pot + +everything.pot: $(pot_files) + cat $^ | $(pofmt) > '$@' %.pot: % - { $(xgettext-main); $(xgettext-prose); } | $(pofmt) > '$@' + { $(xgettext-sh-std); $(xgettext-sh-prose); } | $(pofmt) > '$@' # Install ###################################################################### @@ -142,3 +146,5 @@ clean-hook: PHONY FORCE: PHONY PHONY: .PHONY: FORCE PHONY + +.DELETE_ON_ERROR: diff --git a/doc/treepkg.md b/doc/treepkg.md index 7f7ece1..1e30e33 100644 --- a/doc/treepkg.md +++ b/doc/treepkg.md @@ -67,18 +67,18 @@ Your HOOKLOCALRELEASE script should look like this: source PKGBUILD unset build package check - + fullver=$(full_version ${epoch:-0} ${pkgver} ${pkgrel}) pkgs=() - + # Generate canonical package paths msg "Adding packages to [stage3]..." for name in ${pkgname[@]}; do msg2 "${name} ${fullver}" pkgs+=("${PKGDEST}/${name}-${fullver}-*.pkg.tar.*") done - - # Add the packages to a local + + # Add the packages to a local repo-add ${PKGDEST}/stage3.db.tar.gz ${pkgs[@]} # Stage the packages for later releasing @@ -109,7 +109,7 @@ current one. Thus this will become the build path: > a really long build tree with some circular dependencies you may find > packages buried several times and queued to build before their actuals deps. -## Tips +## Tips `treepkg` accepts two arguments: 1) the temporary work dir and 2) the next depth level it should use (if current equals 0, it'll pass 1). You don't need diff --git a/doc/workflows.md b/doc/workflows.md index f55ae7e..fe3c12e 100644 --- a/doc/workflows.md +++ b/doc/workflows.md @@ -28,7 +28,7 @@ This is how the chroot is configured: * Bind mount /var/cache/pacman/pkg to /chroot/var/cache/pacman/pkg * Put these on system's fstab so I don't have to do it everytime - + * Configure makepkg.conf to PKGDEST=CacheDir and SRCDEST to something on my home. Workflow: @@ -50,7 +50,7 @@ What this allows: * Building many packages in a row without recreating a chroot for every one of them. - + * Knowing that any change you made to the chroot stays as you want (no one touches your makepkg.conf) diff --git a/src/abslibre-tools/createworkdir b/src/abslibre-tools/createworkdir index 99214ab..27b285c 100755 --- a/src/abslibre-tools/createworkdir +++ b/src/abslibre-tools/createworkdir @@ -1,8 +1,10 @@ #!/usr/bin/env bash +set -euE # CreateWorkDir # Creates a dir structure for working with Parabola packages -# Copyright 2010 Nicolás Reynolds +# Copyright (C) 2010 Nicolás Reynolds +# Copyright (C) 2013 Luke Shumaker # ---------- GNU General Public License 3 ---------- @@ -24,41 +26,27 @@ . libremessages . $(librelib conf.sh) load_files libretools -check_vars libretools WORKDIR REPOS ABSLIBREGIT || exit 1 +check_vars libretools WORKDIR REPOS ABSLIBRERECV ABSLIBRESEND -[[ ! -d ${WORKDIR} ]] && { # Create the WORKDIR +trap 'error "Aborting..."' EXIT - msg "Creating WORKDIR on ${WORKDIR}" - mkdir -p ${WORKDIR} ||{ - error "Could not create ${WORKDIR}"; exit 1 - } +msg "Creating WORKDIR at %s..." "$WORKDIR" +mkdir -p "$WORKDIR" -} +msg "Creating staging directory in WORKDIR..." +mkdir -p "$WORKDIR/staging" -for _repo in "${REPOS[@]}"; do # Create the staging dirs +cmd=(gitget -f -p "$ABSLIBRESEND" checkout "$ABSLIBRERECV" "$WORKDIR/abslibre") +if ! "${cmd[@]}"; then + error "Could not clone ABSLibre" + plain "Try running this command:" + echo + printf '%q ' "${cmd[@]}" + echo + exit 1 +fi - [[ ! -d ${WORKDIR}/staging/${_repo} ]] && { - mkdir -p ${WORKDIR}/staging/${_repo} || { - error "Can't create ${WORKDIR}/staging/${_repo}" - exit 1 - } - } +msg "Finished, your packaging directory tree looks like this now:" +ls --color=always "${WORKDIR}"/* -done - -[[ ! -d ${WORKDIR}/abslibre/.git ]] && { - msg "Cloning into ABSLibre" - CMD="git clone ${ABSLIBREGIT} ${WORKDIR}/abslibre" - ${CMD} || { - error "Could not clone ABSLibre" - plain "Try running this command:" - echo - plain "${CMD}" - exit 1 - } -} - -msg "Finished, your packaging dir tree looks like this now:" -ls --color=always ${WORKDIR}/*/* - -exit 0 +trap -- EXIT diff --git a/src/abslibre-tools/diff-unfree b/src/abslibre-tools/diff-unfree index 07f2ca2..af5bbe5 100755 --- a/src/abslibre-tools/diff-unfree +++ b/src/abslibre-tools/diff-unfree @@ -1,7 +1,7 @@ #!/usr/bin/env bash # This script will help you diff a *-libre PKGBUILD against the unfree one # to check for updates. -# Copyright 2010 Nicolás Reynolds +# Copyright (C) 2010 Nicolás Reynolds # ---------- GNU General Public License 3 ---------- @@ -23,16 +23,14 @@ . libremessages . $(librelib conf.sh) load_files libretools -check_vars libretools DIFFTOOL || exit 1 - -cmd=${0##*/} +check_vars libretools DIFFPROG || exit 1 usage() { - echo "Usage: $cmd [community|packages] [unfree-package] [repo]" - echo "Usage: $cmd --help" - echo "Helps you diff build scripts from ABSLibre against (Unfree) ABS." - echo "" - echo "Package name and repo will we guessed if you don't specify them." + print "Usage: %s [community|packages] [unfree-package] [repo]" "${0##*/}" + print "Usage: %s --help" "${0##*/}" + prose "Helps you diff build scripts from ABSLibre against (Unfree) ABS." + echo + prose "Package name and repo will we guessed if you don't specify them." } main() { @@ -68,7 +66,7 @@ main() { pushd "${tmp_dir}" &>/dev/null - msg "Getting diff from $repo/$package..." + msg "Getting diff from %s..." "$repo/$package" svn checkout --depth=empty svn://svn.archlinux.org/$svnrepo &>/dev/null @@ -81,8 +79,8 @@ main() { msg "Diffing files" for _file in ${unfree_dir}/*; do - msg2 "$(basename "${_file}")" - ${DIFFTOOL} "$PWD/$(basename "${_file}")" "${_file}" + msg2 "%s" "$(basename "${_file}")" + ${DIFFPROG} "$PWD/$(basename "${_file}")" "${_file}" done } diff --git a/src/abslibre-tools/libreaddiff b/src/abslibre-tools/libreaddiff index 03d0ad0..ebd749d 100755 --- a/src/abslibre-tools/libreaddiff +++ b/src/abslibre-tools/libreaddiff @@ -23,10 +23,12 @@ check_vars libretools WORKDIR for arg in "$@" ; do case "$arg" in -h|--h|--he|--hel|--help|-\?) - echo 'Usage: libreaddiff repo [arch] - -This script outputs a diff of package names and versions in repo -between pacman'\''s sync db and abslibre checkout.' >&2 + { + print 'Usage: %s repo [arch]' "${0##*/}" + echo + prose "This script outputs a diff of package names and versions + in repo between pacman's sync db and abslibre checkout." + } >&2 exit 0 ;; esac @@ -72,14 +74,14 @@ except StopIteration: load_PKGBUILD "$f/PKGBUILD" || continue is_here=false for arc in ${arch[@]} ; do - if [ "$arc" = "any" -o "$arc" = "$CARCH" ] ; then + if [[ "$arc" == "any" ]] || [[ "$arc" == "$CARCH" ]] ; then is_here=true break fi done - if [ "$is_here" = "true" ] ; then - for name in ${pkgname[@]} ; do - if [ -z "$epoch" ] ; then + if [[ "$is_here" == "true" ]] ; then + for name in "${pkgname[@]}" ; do + if [[ -z "$epoch" ]] ; then echo $name-$pkgver-$pkgrel else echo $name-$epoch:$pkgver-$pkgrel diff --git a/src/abslibre-tools/librerelease b/src/abslibre-tools/librerelease index 5913670..8b1e05f 100755 --- a/src/abslibre-tools/librerelease +++ b/src/abslibre-tools/librerelease @@ -2,23 +2,23 @@ # Librerelease # Uploads packages into [staging] -# Copyright 2010 Nicolás Reynolds -# Copyright 2013 Luke Shumaker +# Copyright (C) 2010 Nicolás Reynolds +# Copyright (C) 2013-2014 Luke Shumaker # For just the create_signature() function: -# Copyright (c) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> -# Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> -# Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> -# Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> -# Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> -# Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk> -# Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.org> -# Copyright (c) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> -# Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> -# Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> -# Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> -# Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> -# Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk> -# Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.org> +# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2005 by Aurelien Foret <orelien@chez.com> +# Copyright (C) 2006 by Miklos Vajna <vmiklos@frugalware.org> +# Copyright (C) 2005 by Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 by Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 by Andras Voroskoi <voroskoi@frugalware.org> +# Copyright (C) 2006-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2005 by Aurelien Foret <orelien@chez.com> +# Copyright (C) 2006 by Miklos Vajna <vmiklos@frugalware.org> +# Copyright (C) 2005 by Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 by Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 by Andras Voroskoi <voroskoi@frugalware.org> # # This file is part of Parabola. # @@ -38,25 +38,24 @@ . libremessages . $(librelib conf.sh) -function usage { - print "Usage: %s [OPTIONS]" "${0##*/}" - echo - print 'This script uploads packages on $WORKDIR/stagging' - print "to parabola server." - echo - print "Options:" - print ' -c Clean packages on $WORKDIR/staging' - print " -l Only list packages but not upload them" - print " -n Dry-run; don't actually do anything" - print " -h Show this message" -} - -function list_packages { - find "$WORKDIR/staging/" -mindepth 1 -type d -not -empty -printf '%f\n' | sort | - while read -r repo; do - msg2 "$repo" - find "${WORKDIR}/staging/${repo}" -type f -printf "%f\n" | sort - done +dryrun="" +upload_only=false +readonly rsync_flags=( + --no-group + --no-perms + --copy-links + --hard-links + --partial + --human-readable + --progress + -e ssh +) + +# Functions #################################################################### + +list0_files() { + find -L "${WORKDIR}/staging" -type f \ + -exec realpath -z --relative-to="${WORKDIR}/staging" {} + } # This function is taken almost verbatim from makepkg @@ -70,7 +69,7 @@ create_signature() { SIGNWITHKEY="-u ${GPGKEY}" fi # The signature will be generated directly in ascii-friendly format - gpg --detach-sign --use-agent ${SIGNWITHKEY} "$filename" &>/dev/null || ret=$? + gpg --detach-sign --use-agent ${SIGNWITHKEY} "$filename" || ret=$? if (( ! ret )); then @@ -81,67 +80,81 @@ create_signature() { fi } -function sign_packages { - if [ -z "${GPG_AGENT_INFO}" ]; then - warning "It's better to use gpg-agent to sign packages in batches" +sign_packages() { + if [[ -z "${GPG_AGENT_INFO}" ]]; then + warning "It's better to have \`%s\` running to sign packages in batches" 'gpg-agent --daemon' fi - for package in $(find "${WORKDIR}/staging/" -type f -iname '*.pkg.tar.?z'); do - if [ -f "${package}.sig" ]; then - msg2 "Package signature found, verifying..." + for file in $(find "${WORKDIR}/staging/" -type f -not -iname '*.sig'); do + if [[ -f "${file}.sig" ]]; then + msg2 "File signature found, verifying..." # Verify that the signature is correct, else remove for re-signing - if ! gpg --quiet --verify "${package}.sig" >/dev/null 2>&1; then + if ! gpg --quiet --verify "${file}.sig" >/dev/null 2>&1; then error "Failed! Re-signing..." - rm -f "${package}.sig" + rm -f "${file}.sig" fi fi - if ! [ -f "${package}.sig" ]; then - create_signature "$package" || return 2 + if ! [[ -f "${file}.sig" ]]; then + create_signature "$file" || return 2 fi done } -# Remove everything that's not a package or a signature -function clean_non_packages { - find $WORKDIR/staging/ -type f \ - \! -iname "*.pkg.tar.?z" -a \! -iname "*.pkg.tar.?z.sig" \ - -delete -} - # Clean everything if not on dry-run mode -function clean { +clean_files() { + local file_list=$1 + + local rmcmd=(rm -fv) if [[ -n "${dryrun}" ]]; then - : - else - msg "Removing files from local staging directory" - # use '-exec rm' instead of '-delete' to be verbose - find "${WORKDIR}/staging" -type f -exec rm -fv {} + + rmcmd=(printf "$(_ "removed '%s' (dry-run)")\n") fi + + msg "Removing files from local staging directory" + cd "${WORKDIR}/staging" && xargs -0r -a "$file_list" "${rmcmd[@]}" + cd "${WORKDIR}/staging" && find . -mindepth 1 -type d -empty \ + -exec rmdir -p {} + 2>/dev/null } -function main { - if [ -w / ]; then +################################################################################ + +usage() { + print "Usage: %s [OPTIONS]" "${0##*/}" + echo + prose 'This script uploads packages on $WORKDIR/stagging + to parabola server.' + echo + print "Options:" + flag '-c' 'Clean; delete packages in $WORKDIR/staging' + flag '-l' "List; list packages but not upload them" + flag '-u' "Upload-only; do not run db-update on the server" + + flag '-n' "Dry-run; don't actually do anything" + flag '-h' "Show this message" +} + +main() { + if [[ -w / ]]; then error "This program should be run as regular user" return 1 fi # Parse options - local dryrun="" local mode="release_packages" - while getopts 'clnh' arg; do + while getopts 'clunh' arg; do case $arg in c) mode=clean ;; - l) mode=list_packages ;; + l) mode=pretty_print_packages ;; + u) upload_only=true ;; n) dryrun="--dry-run" ;; h) mode=usage ;; - *) usage >/dev/stderr; return 1 ;; + *) usage >&2; return 1 ;; esac done shift $(($OPTIND - 1)) if [[ $# != 0 ]]; then - usage >/dev/stderr + usage >&2 return 1 fi @@ -154,41 +167,66 @@ function main { check_vars makepkg GPGKEY load_files libretools check_vars libretools WORKDIR REPODEST || return 1 - # The following variables are actually optional + REPODEST+='/staging/' + # The following settings are actually optional #check_vars libretools HOOKPRERELEASE HOOKPOSTRELEASE || return 1 - lock 10 "${WORKDIR}/staging.lock" 'Waiting for an exclusive lock on the staging directory' "$mode" } -function release_packages { +# The different modes (sans 'usage') ########################################### + +pretty_print_packages() { + find "$WORKDIR/staging/" -mindepth 1 -maxdepth 1 -type d -not -empty | sort | + while read -r path; do + msg2 "${path##*/}" + cd "$path" + find -L . -type f | sed 's|^\./| |' | sort + done +} + +clean() { + lock 10 "${WORKDIR}/staging.lock" \ + 'Waiting for an exclusive lock on the staging directory' + + local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)" + trap "$(printf 'rm -f -- %q' "$file_list")" EXIT + list0_files > "$file_list" + + lock_close 10 + + clean_files "$file_list" +} + +release_packages() { if [[ -n $HOOKPRERELEASE ]]; then msg "Running HOOKPRERELEASE..." + plain '%s' "${HOOKPRERELEASE}" bash -c "${HOOKPRERELEASE}" fi - clean_non_packages + lock 10 "${WORKDIR}/staging.lock" \ + 'Waiting for an exclusive lock on the staging directory' + sign_packages || return 1 # Make the permissions of the packages 644 otherwise the user will get access # denied error when they try to download (rsync --no-perms doesn't seem to # work). - find ${WORKDIR}/staging -type f -exec chmod 644 {} \; - find ${WORKDIR}/staging -type d -exec chmod 755 {} \; + find ${WORKDIR}/staging -type f -exec chmod 644 {} + + find ${WORKDIR}/staging -type d -exec chmod 755 {} + + + local file_list="$(mktemp -t ${0##*/}.XXXXXXXXXX)" + trap "$(printf 'rm -f -- %q' "$file_list")" EXIT + list0_files > "$file_list" - msg "%s to upload" $(du -h -d 0 ${WORKDIR}/staging | tr "\t" " " | cut -d" " -f1) + lock_close 10 + + msg "%s to upload" "$(cd "${WORKDIR}/staging" && du -hc --files0-from="$file_list" | sed -n '$s/\t.*//p')" msg "Uploading packages..." - if ! rsync --recursive \ - ${dryrun} \ - --no-group \ - --no-perms \ - --copy-links \ - --hard-links \ - --partial \ - --prune-empty-dirs \ - --human-readable \ - --progress \ - -e "ssh " \ + xargs -0r -a "$file_list" dirname -z | ssh ${REPODEST%%:*} "$(printf 'mkdir -p -- %q && cd %q && xargs -0r mkdir -pv --' "${REPODEST#*:}" "${REPODEST#*:}")" + if ! rsync ${dryrun} "${rsync_flags[@]}" \ + -0 --files-from="$file_list" \ ${WORKDIR}/staging \ ${REPODEST}/ then @@ -196,13 +234,18 @@ function release_packages { return 1 fi - clean + clean_files "$file_list" + + if $upload_only; then + return 0 + fi msg "Running db-update on repos" - ssh ${REPODEST%%:*} dbscripts/db-update + ssh ${REPODEST%%:*} "$(printf 'STAGING=%q dbscripts/db-update' "${REPODEST#*:}")" if [[ -n $HOOKPOSTRELEASE ]]; then msg "Running HOOKPOSTRELEASE..." + plain '%s' "${HOOKPOSTRELEASE}" bash -c "${HOOKPOSTRELEASE}" fi diff --git a/src/abslibre-tools/librestage b/src/abslibre-tools/librestage index 57846fc..610de50 100755 --- a/src/abslibre-tools/librestage +++ b/src/abslibre-tools/librestage @@ -2,8 +2,8 @@ # LibreStage # Prepares packages for upload -# Copyright 2010-2011 Nicolás Reynolds -# Copyright 2013 Luke Shumaker +# Copyright (C) 2010-2011 Nicolás Reynolds +# Copyright (C) 2013-2014 Luke Shumaker # # This file is part of Parabola. # @@ -23,14 +23,13 @@ . libremessages . $(librelib conf.sh) -cmd=${0##*/} usage() { - print "Usage: %s REPO [REPO2 REPO3...]" "$cmd" + print "Usage: %s REPO [REPO2 REPO3...]" "${0##*/}" print "Stages the package(s) build by ./PKGBUILD for upload." echo - print "The package(s) are staged for the named repositories." - print "It is in general a bad idea to stage a package on multiple" - print "repositories, but it supported by this tool." + prose "The package(s) are staged for the named repositories. + It is in general a bad idea to stage a package on multiple + repositories, but it supported by this tool." } main() { @@ -43,12 +42,12 @@ main() { while getopts 'h' arg; do case $arg in h) usage; return 0;; - *) usage >/dev/stderr; return 1;; + *) usage >&2; return 1;; esac done repos=("$@") if [[ ${#repos[@]} -eq 0 ]]; then - usage >>/dev/stderr + usage >&2 return 1; fi @@ -60,14 +59,16 @@ main() { # Load configuration load_files libretools check_vars libretools WORKDIR ARCHES || return 1 - load_files makepkg # for PKGDEST, which is optional + load_files makepkg # for PKGDEST and SRCDEST, which are optional + load_files librefetch # for MIRRORS, which is optional # Load the PKGBUILD load_PKGBUILD # Now for the main routine. staged=false - slock 10 "${WORKDIR}/staging.lock" 'Waiting for a shared lock on the staging directory' + slock 10 "${WORKDIR}/staging.lock" \ + 'Waiting for a shared lock on the staging directory' for CARCH in "${ARCHES[@]}" any; do for _pkgname in "${pkgname[@]}"; do pkgfile=${_pkgname}-$(get_full_version $_pkgname)-${CARCH}${PKGEXT} @@ -79,7 +80,7 @@ main() { pkgpath="$(readlink -f "$pkgpath")" fi - msg "Found ${pkgfile}" + msg 'Found package: %s' "${pkgfile}" canonical="" # is empty for the first iteration, set after that for repo in "${repos[@]}"; do @@ -101,10 +102,44 @@ main() { done done + for netfile in "${source[@]}"; do + for mirror in "${MIRRORS[@]}"; do + srcurl=${netfile#*::} + if [[ "$srcurl" == "$mirror"* ]]; then + if [[ $netfile = *::* ]]; then + srcname=${netfile%%::*} + else + srcname=${netfile##*/} + fi + + srcpath='' + for path in "./$srcname" "${SRCDEST:-.}/$srcname"; do + if [[ -f "$path" ]]; then + srcpath="$path" + break + fi + done + if [[ -n "$srcpath" ]]; then + msg "Found generated source file: %s" "$srcname" + dest="${WORKDIR}/staging/other/${srcurl##"$mirror"}" + mkdir -p -- "${dest%/*}" + if cp "$srcpath" "$dest"; then + msg2 "%s staged on [%s]" "$srcname" other + staged=true + else + error "Can't put %s on [%s]" "$srcname" other + return 1 + fi + fi + break + fi + done + done + if $staged ; then return 0 else - error "No package was staged" + error "Nothing was staged" return 1 fi } @@ -1,34 +1,34 @@ #!/usr/bin/env bash -# Copyright 2010 Joshua Ismael -# Copyright 2010 Nicolás Reynolds -# Copyright 2013 Luke Shumaker +# Copyright (C) 2010 Joshua Ismael +# Copyright (C) 2010 Nicolás Reynolds +# Copyright (C) 2013-2014 Luke Shumaker # -# This file is part of Parabola. +# This file is part of Parabola. # -# Parabola is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. - -# Parabola is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# Parabola is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Parabola is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with Parabola. If not, see <http://www.gnu.org/licenses/>. +# You should have received a copy of the GNU General Public License +# along with Parabola. If not, see <http://www.gnu.org/licenses/>. . libremessages -cmd=${0##*/} usage() { - echo "Usage: $cmd [-h] pkgname-from-aur1 [pkgname-from-aur2 ...]" + print "Usage: %s [-h] PKGNAME [PKGNAME2 PKGNAME3...]" "${0##*/}" + print "Downloads packages from the AUR, and does basic freedom checks." echo - echo "This script will download packages from AUR to the current" - echo "directory and check their license for nonfree issues. This does" - echo "not mean that they are free; they may be incorrectly labeled, or" - echo "have other freedom issues. It's a tool to help Parabola" - echo "packagers, not to help users install things directly from AUR." + prose "This script will download packages from AUR to the current + directory and check their license for nonfree issues. This does + not mean that they are free; they may be incorrectly labeled, or + have other freedom issues. It's a tool to help Parabola + packagers, not to help users install things directly from AUR." } main() { @@ -45,7 +45,7 @@ main() { . $(librelib conf.sh) load_files libretools - check_vars libretools DIFFTOOL || exit 1 + check_vars libretools DIFFPROG || exit 1 local startdir="$(pwd)" local missing_deps=() @@ -88,7 +88,7 @@ main() { if ! cmp -s "${copy_old}/${file}" "${copy_new}/${file}" ; then warning "%s != %s" "${copy_old}/${file}" "${copy_new}/${file}" diffed=true - "${DIFFTOOL}" "${copy_old}/${file}" "${copy_new}/${file}" + "${DIFFPROG}" "${copy_old}/${file}" "${copy_new}/${file}" fi done if $diffed; then @@ -107,26 +107,7 @@ main() { ################################################################ pkgbuild-check-nonfree -c - case $? in - 0) :;; - 15) warning "This PKGBUILD links to known unfree packages";; - *) warning "pkgbuild-check-nonfree failed to run";; - esac - - ################################################################ - - local s=0 - pkgbuild-check-licenses || s=$? - for i in 1 2 4; do - if [[ $i -eq $(($s & $i)) ]]; then - case $i in - 1) warning "pkgbuild-check-licenses encountered an error";; - 2) warning "This PKGBUILD has an uncommon license";; - 4) warning "This PKGBUILD has a known nonfree license";; - esac - fi - done - unset s + pkgbuild-summarize-nonfree $? ################################################################ diff --git a/src/chroot-tools/Makefile b/src/chroot-tools/Makefile index 0540636..97ca688 100644 --- a/src/chroot-tools/Makefile +++ b/src/chroot-tools/Makefile @@ -1,11 +1,13 @@ # These files are coming from devtools copy_files = makechrootpkg.sh.in mkarchroot.in arch-nspawn.in # These are programs that we will use internally, but shouldn't be in PATH -libexecs = mkarchroot arch-nspawn distcc-tool chcleanup +libexecs = mkarchroot arch-nspawn distcc-tool chcleanup indent no-progs = $(libexecs) # These are the shell libraries we will use libs = makechrootpkg.sh $(wildcard hooks-*.sh) +pots = $(libexecs) $(libs) + pkglibexecdir = $(libexecdir)/libretools/chroot clean_files = makechrootpkg.sh.ugly* *~ include ../../common.mk @@ -25,20 +27,19 @@ makechrootpkg.sh.in: %.sh.in: $(devtoolsdir)/%.in cp $< $@ makechrootpkg.sh.ugly: %.ugly: %.in %.patch Makefile cp $*.in $@ - @echo 'PATCH $@ $*.patch'; patch $@ $*.patch || { rm -f -- '$@'; false; } + @echo 'PATCH $@ $*.patch'; patch $@ $*.patch makechrootpkg.sh: %: %.ugly Makefile - @echo 'EDIT < $< > $@'; $(edit) <'$<' >'$@' || { rm -f -- '$@'; false; } - @echo 'INDENT $@'; $(call indent,$@) || { rm -f -- '$@'; false; } + @echo 'EDIT < $< > $@'; $(edit) <'$<' >'$@' + @echo 'INDENT $@'; $(call indent,$@) mkarchroot: mkarchroot.in Makefile @echo '< $< M4_EDIT | SED > $@' - @<'$<' $(edit) | sed 's|arch-nspawn|$$(librelib chroot/&)|' >'$@' || { rm -f -- '$@'; false; } - @echo 'CHMOD $<'; chmod 755 "$@" || { rm -f -- '$@'; false; } + @<'$<' $(edit) | sed -e 's|arch-nspawn|$$(librelib chroot/&)|' -e 's/pacstrap/env -i &/' >'$@' + @echo 'CHMOD $<'; chmod 755 "$@" archroot: %: %.in Makefile @echo "GEN $@" - @$(edit) <"$<" >"$@" || { rm -f -- '$@'; false; } - @chmod 755 "$@" || { rm -f -- '$@'; false; } + @$(edit) <"$<" >"$@" + @chmod 755 "$@" -distcc-tool.pot: distcc-tool - xgettext --omit-header -i --from-code=UTF-8 -L shell --keyword={error,errusage} -o $@ $< +distcc-tool.pot: xgettext-keywords-sh+=--keyword=errusage diff --git a/src/chroot-tools/chcleanup b/src/chroot-tools/chcleanup index a43065b..9ad121e 100755 --- a/src/chroot-tools/chcleanup +++ b/src/chroot-tools/chcleanup @@ -1,6 +1,6 @@ #!/usr/bin/env bash set -eE -# (c) Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) Nicolás Reynolds <fauno@parabola.nu> # Released under GPLv3 # # Performs chroot cleanup smartly, it only removes the unneeded packages or @@ -53,19 +53,18 @@ fi source /etc/libretools.d/chroot.conf # If we're running makepkg -if [ -f PKGBUILD ]; then +if [[ -f PKGBUILD ]]; then export CARCH="$(. /etc/makepkg.conf; printf '%s' "$CARCH")" - source PKGBUILD + source ./PKGBUILD CHROOTEXTRAPKG+=("${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}") fi msg "Cleaning chroot..." -msg2 "Fetching updated package lists..." -pacman -Sy || { - warning "There was an error updating package lists, ignoring." -} + +# Sync the local repo with pacman +cp /repo/repo.db /var/lib/pacman/sync/repo.db # Setup the temporary directory TEMPDIR="$(mktemp --tmpdir -d $(basename $0).XXXXX)" diff --git a/src/chroot-tools/distcc-tool b/src/chroot-tools/distcc-tool index 7633029..9f78ead 100755 --- a/src/chroot-tools/distcc-tool +++ b/src/chroot-tools/distcc-tool @@ -2,7 +2,7 @@ # -*- tab-width: 4; sh-basic-offset: 4 -*- # distcc-tool -# Copyright 2013 Luke Shumaker +# Copyright (C) 2013-2014 Luke Shumaker # # This file is part of Parabola. # @@ -32,6 +32,8 @@ if ! type gettext &>/dev/null; then gettext() { echo "$@"; } fi +q0="$(printf '%q' "$0")" # quoted $0 + panic() { echo "$(gettext 'panic: malformed call to internal function')" >&2 exit 1 @@ -50,7 +52,7 @@ print() { } usage() { - print "Usage: $0 COMMAND [COMMAND-ARGS]" + print "Usage: %s COMMAND [COMMAND-ARGS]" "$q0" print "Tool for using distcc within a networkless chroot" echo print "Commands:" @@ -125,7 +127,7 @@ parse_DISTCC_HOSTS() { *@*) # SSH_HOST doesn't allow custom port numbers, and even if it # did, ssh would complain about MITM. Instead, we'll count on - # ssh ProxyCommand being configured to used `client`. + # ssh ProxyCommand being configured to use `client`. newhosts+=("$HOSTSPEC") ;; # GLOBAL_OPTION @@ -135,7 +137,7 @@ parse_DISTCC_HOSTS() { ;; # ZEROCONF +zeroconf) - error "%s does not support the +zeroconf option" "$0" + error "%s does not support the +zeroconf option" "$q0" exit 1 ;; # TCP_HOST or OLDSTYLE_TCP_HOST @@ -159,7 +161,7 @@ parse_DISTCC_HOSTS() { # set up port forwaring if $forward_ports; then - socat TCP-LISTEN:${newport},fork SYSTEM:"$0 client $HOSTID ${PORT:-3632}" & + socat TCP-LISTEN:${newport},fork SYSTEM:"$q0 client $HOSTID ${PORT:-3632}" & pids+=($!) fi @@ -222,8 +224,8 @@ odaemon() { local chrootpath=$1 umask 111 - socat UNIX-LISTEN:"$chrootpath/socket",fork SYSTEM:"$0 server" & - trap "kill -- $!; rm -f '$chrootpath/socket'" EXIT + socat UNIX-LISTEN:"$chrootpath/socket",fork SYSTEM:"$q0 server" & + trap "kill -- $!; rm -f -- $(printf '%q' "$chrootpath/socket")" EXIT wait } diff --git a/src/chroot-tools/hooks-chcleanup.sh b/src/chroot-tools/hooks-chcleanup.sh index 09e6dd9..86c872c 100644 --- a/src/chroot-tools/hooks-chcleanup.sh +++ b/src/chroot-tools/hooks-chcleanup.sh @@ -1,16 +1,15 @@ #!/usr/bin/env bash set -euE -hooks_pre_build+=("clean_chroot") +hook_pre_build+=("clean_chroot") clean_chroot() ( set +x local copydir=$1 if $INCHROOT; then - cd /build + cd /startdir sudo -u nobody "$(librelib chroot/chcleanup)" else - librechroot -l "$copydir" clean-pkgs + librechroot "${librechroot_flags[@]}" clean-pkgs fi - r=$?; echo clean_chroot returning $r; return $r ) diff --git a/src/chroot-tools/hooks-check.sh b/src/chroot-tools/hooks-check.sh index e8120b8..2702f95 100644 --- a/src/chroot-tools/hooks-check.sh +++ b/src/chroot-tools/hooks-check.sh @@ -1,30 +1,11 @@ #!/usr/bin/env bash set -euE -hook_check_pkgbuild+=("check_pkgbuild_dependencies") -check_pkgbuild_dependencies() { +hook_check_pkgbuild+=("check_pkgbuild_nonfree") +check_pkgbuild_nonfree() { local s=0 sudo -EH -u "$LIBREUSER" pkgbuild-check-nonfree -f || s=$? - case $s in - 0) :;; - 15) error "This PKGBUILD links to known unfree packages"; return 1;; - *) warning "pkgbuild-check-nonfree failed to run";; - esac -} - -hook_check_pkgbuild+=("check_pkgbuild_license") -check_pkgbuild_license() { - local s=0 - sudo -EH -u "$LIBREUSER" pkgbuild-check-licenses -f || s=$? - for i in 1 2 4; do - if [[ $i -eq $(($s & $i)) ]]; then - case $i in - 1) warning "pkgbuild-check-licenses encountered an error";; - 2) warning "This PKGBUILD has an uncommon license";; - 4) error "This PKGBUILD has a known nonfree license"; ret=1;; - esac - fi - done + pkgbuild-summarize-nonfree $s } #hook_check_pkgbuild+=("check_pkgbuild_namcap") diff --git a/src/chroot-tools/hooks-distcc.sh b/src/chroot-tools/hooks-distcc.sh index 9e42242..d8d708a 100644 --- a/src/chroot-tools/hooks-distcc.sh +++ b/src/chroot-tools/hooks-distcc.sh @@ -75,7 +75,7 @@ distcc_stop() { if [[ -f "$copydir/run/distcc-tool.pid" ]]; then - odaemon=$(cat "$copydir/distcc-tool.pid") + odaemon=$(< "$copydir/distcc-tool.pid") kill -- "$odaemon" rm -f -- \ diff --git a/src/chroot-tools/indent b/src/chroot-tools/indent new file mode 100755 index 0000000..0e2d0e0 --- /dev/null +++ b/src/chroot-tools/indent @@ -0,0 +1,35 @@ +#!/usr/bin/env perl +use warnings; +use strict; +use constant BUFFER_SIZE => 40; +binmode(STDIN); +binmode(STDOUT); + +exit(1) if ($#ARGV != 0); +my $indent = $ARGV[0]; + +my $print_indent = 1; +my $buffer; +my $size; +my $c; +while (1) { + $size = sysread(STDIN, $buffer, BUFFER_SIZE); + last if ($size < 1); + for (0..$size-1) { + $c = substr($buffer, $_, 1); + if ($c eq "\n") { + syswrite(STDOUT, $indent) if ($print_indent); + # XXX: SYSTEMD-STDOUT HACK + #syswrite(STDOUT, $c, 1); + syswrite(STDOUT, "\r\n", 2); + $print_indent = 1; + } elsif ($c eq "\r") { + syswrite(STDOUT, $c, 1); + $print_indent = 1; + } else { + syswrite(STDOUT, $indent) if ($print_indent); + syswrite(STDOUT, $c, 1); + $print_indent = 0; + } + } +} diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot index 0b3ad43..051148d 100755 --- a/src/chroot-tools/librechroot +++ b/src/chroot-tools/librechroot @@ -2,9 +2,9 @@ set -euE # librechroot -# Copyright 2010 Nicolás Reynolds -# Copyright 2011 Joshua Haase -# Copyright 2012-2013 Luke Shumaker +# Copyright (C) 2010 Nicolás Reynolds +# Copyright (C) 2011 Joshua Haase +# Copyright (C) 2012-2014 Luke Shumaker # # This file is part of Parabola. # @@ -21,10 +21,11 @@ set -euE # You should have received a copy of the GNU General Public License # along with Parabola. If not, see <http://www.gnu.org/licenses/>. -# HACKING: if a command is added or removed, it must be changed in 3 places: +# HACKING: if a command is added or removed, it must be changed in 4 places: # - the usage() text # - the commands=() array -# - the case statement in main() +# - the case statement in main() that checks the number of arguments +# - the case statement in main() that runs them . $(librelib conf) load_files chroot @@ -88,16 +89,16 @@ usage() { unless the copy name is manually specified as an absolute path, in which case, that path is used.' echo - prose 'The current settings for the above varibles are:' + prose 'The current settings for the above variables are:' printf ' CHROOTDIR : %s\n' "${CHROOTDIR:-$(_ 'ERROR: NO SETTING')}" printf ' CHROOT : %s\n' "${CHROOT:-$(_ 'ERROR: NO SETTING')}" printf ' COPY : %s\n' "$COPY" printf ' rootdir : %s\n' "${rootdir:-$(_ 'ERROR')}" printf ' copydir : %s\n' "${copydir:-$(_ 'ERROR')}" echo - prose 'If the chroot, or copy does not exist, it will be created + prose 'If the chroot or copy does not exist, it will be created automatically. A chroot by default contains the packages in the - group "base-devel", and any packages named in $CHROOTEXTRAPKG. + group "base-devel" and any packages named in $CHROOTEXTRAPKG. Unless the `-C` or `-M` flags are used, the configuration files that this program installs are the stock versions supplied in the packages, not the versions from your host system. Other tools @@ -136,8 +137,8 @@ usage() { flag 'update' 'Like `pacman -Syu`' flag 'clean-pkgs' 'Remove all packages from the chroot copy that are not in base-devel, $CHROOTEXTRAPKG, or named - as a dependency in the file `/build/PKGBUILD` in - the chroot copy' + as a dependency in the file `/startdir/PKGBUILD` + in the chroot copy' print ' Other:' flag "run $(_ CMD...)" 'Run CMD in the chroot copy' flag 'enter' 'Enter an interactive shell in the chroot copy' @@ -150,7 +151,7 @@ readonly commands=( run enter clean-repo help ) -# set $rootdir and $copydir; blank them on error +# Print code to set $rootdir and $copydir; blank them on error calculate_directories() { # Don't assume that CHROOTDIR or CHROOT are set, # but assume that COPY is set. @@ -174,12 +175,37 @@ calculate_directories() { declare -p copydir } +check_mountpoint() { + local file=$1 + local mountpoint="$(df -P "$file"|sed '1d;s/.*\s//')" + local mountopts=($(LC_ALL=C mount|awk "{ if (\$3==\"$mountpoint\") { gsub(/[(,)]/, \" \", \$6); print \$6 } }")) + ! in_array nosuid "${mountopts[@]}" && ! in_array noexec "${mountopts[@]}" +} + arch_nspawn_flags=() sysd_nspawn_flags=() arch-nspawn() { local copydir=$1; shift + # XXX: SYSTEMD-STDOUT HACK + if [[ -t 1 ]]; then + cmd=("$@") + else + # This perl script is similar to `sed 's|\n|\r\n|g'`, (or, more + # correctly, `sed 's|$|\r|'`) but it does't line-buffer. + local perlcmd=' +my $size; +my $buffer; +while(1) { + $size=sysread(STDIN, $buffer, 40); + last if ($size < 1); + $buffer =~ s/\n/\r\n/g; + syswrite(STDOUT, $buffer); +}' + cmd=(bash --noprofile --norc -c "set -o pipefail; $(printf '%q ' "$@") |& perl -e $(printf '%q' "$perlcmd")") + fi + set +u # if an array is empty, it counts as unbound - "$_arch_nspawn" "${arch_nspawn_flags[@]}" "$copydir" "${sysd_nspawn_flags[@]}" -- "$@" + "$_arch_nspawn" "${arch_nspawn_flags[@]}" "$copydir" "${sysd_nspawn_flags[@]}" -- "${cmd[@]}" set -u } @@ -197,22 +223,65 @@ main() { C|M) arch_nspawn_flags+=(-$opt "$OPTARG");; w) sysd_nspawn_flags+=("--bind=$OPTARG");; r) sysd_nspawn_flags+=("--bind-ro=$OPTARG");; - *) usage >/dev/stderr; return 1;; + *) usage >&2; return 1;; esac done shift $(($OPTIND - 1)) if [[ $# -lt 1 ]]; then error "Must specify a command" - usage >/dev/stderr + usage >&2 return 1 fi mode=$1 if ! in_array "$mode" "${commands[@]}"; then error "Unrecognized command: %s" "$mode" - usage >/dev/stderr + usage >&2 return 1 fi shift + case "$mode" in + noop|make|sync|delete|update|enter|clean-pkgs|clean-repo) + if [[ $# -gt 0 ]]; then + error 'Command `%s` does not take any arguments: %s' "$mode" "$*" + usage >&2 + return 1 + fi + :;; + install-file) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one file' "$mode" + usage >&2 + return 1 + else + local missing=() + local file + for file in "$@"; do + if ! [[ -f $file ]]; then + missing+=("$file") + fi + done + if [[ ${#missing[@]} -gt 0 ]]; then + error "%s: file(s) not found: %s" "$mode" "${missing[*]}" + return 1 + fi + fi + :;; + install-name) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one package name' "$mode" + usage >&2 + return 1 + fi + :;; + run) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one argument' "$mode" + usage >&2 + return 1 + fi + :;; + esac + if [[ $mode == help ]]; then usage @@ -236,11 +305,28 @@ main() { umask 0022 + # XXX: SYSTEMD-STDIN HACK + if ! [[ -t 0 ]]; then + error "Input is not a TTY" + plain "https://labs.parabola.nu/issues/420" + plain "https://bugs.freedesktop.org/show_bug.cgi?id=70290" + prose "Due to a bug in systemd-nspawn, redirecting stdin is not + supported. We have been able to mitigate the problems + with redirecting stdout, but until the bug is fixed, + redirecting stdin will only end in pain." >&2 + return 1 + fi + # Keep this lock as long as we are running # Note that '9' is the same FD number as in mkarchroot et al. lock 9 "$copydir.lock" \ "Waiting for existing lock on chroot copy to be released: [%s]" "$COPY" + if ! check_mountpoint "$copydir.lock"; then + error "Chroot copy is mounted with nosuid or noexec options: [%s]" "$COPY" + return 1 + fi + if [[ ! -d $rootdir ]]; then msg "Creating 'root' copy for chroot [%s]" "$CHROOT" set +u # if an array is empty, it counts as unbound @@ -256,10 +342,12 @@ main() { mkdir -p "$copydir/etc/libretools.d" { - if [[ -n ${CHROOTEXTRAPKG[*]:-} ]]; then - declare -p CHROOTEXTRAPKG | sed -r 's/declare( -.)* //' + if [[ ${#CHROOTEXTRAPKG[*]} -eq 0 ]]; then + echo 'CHROOTEXTRAPKG=()' else - printf 'CHROOTEXTRAPKG=()\n' + printf 'CHROOTEXTRAPKG=(' + printf '%q ' "${CHROOTEXTRAPKG[@]}" + printf ')\n' fi } > "$copydir"/etc/libretools.d/chroot.conf @@ -293,15 +381,17 @@ main() { arch-nspawn "$copydir" pacman -Sy "$@" ;; update) - arch-nspawn "$copydir" pacman -Syu --noconfirm + # umount resolv.conf so that it can be upgraded, if nescessary + # this disables DNS, so fetch everything first + arch-nspawn "$copydir" bash -c 'pacman -Syuw --noconfirm && umount /etc/resolv.conf && pacman -Su --noconfirm' ;; clean-pkgs) trap "rm -f '$copydir'/bin/chcleanup '$copydir'/chrootexec" EXIT install -m755 "$(librelib chroot/chcleanup)" "$copydir/bin/chcleanup" printf '%s\n' \ '#!/bin/bash' \ - 'mkdir -p /build' \ - 'cd /build' \ + 'mkdir -p /startdir' \ + 'cd /startdir' \ '/bin/chcleanup' \ > "$copydir/chrootexec" chmod 755 "$copydir/chrootexec" diff --git a/src/chroot-tools/libremakepkg b/src/chroot-tools/libremakepkg index a59315b..26080bc 100755 --- a/src/chroot-tools/libremakepkg +++ b/src/chroot-tools/libremakepkg @@ -2,9 +2,9 @@ set -euE # libremakepkg -# Copyright 2010-2011 Nicolás Reynolds -# Copyright 2011 Joshua Ismael Haase Hernández -# Copyright 2012-2013 Luke Shumaker +# Copyright (C) 2010-2011 Nicolás Reynolds +# Copyright (C) 2011 Joshua Ismael Haase Hernández +# Copyright (C) 2012-2014 Luke Shumaker # # This file is part of Parabola. # @@ -25,15 +25,18 @@ set -euE . $(librelib messages) . $(librelib chroot/makechrootpkg.sh) +set -o pipefail shopt -s nullglob umask 0022 # Global variables: +readonly _indent="$(librelib chroot/indent)" readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false) NONET=true # can be changed with the -N flag -# {SRC,LOG,PKG}DEST set at runtime by makepkg.conf +# {PKG,SRC,SRCPKG,LOG}DEST set at runtime by makepkg.conf # MAKEFLAGS, PACKAGER set at runtime by makepkg.conf # LIBREUSER, LIBREHOME are set by conf.sh +librechroot_flags=() # Hooks ######################################################################## @@ -47,6 +50,25 @@ hook_check_pkg=(:) # Boring/mundane functions ##################################################### +indent() { + "$_indent" ' | ' +} + +# Usage: _check_perms_dir $directory +# Make sure that $directory is readable and executable (searchable) by 'nobody' +check_directory_permissions() ( + local dir=$1 + # `cd` to the directory, then test `.`; that way if parent + # directories aren't readable, we aren't testing for that. We + # only need the last element in `$dir`. + cd "$dir" + if ! sudo -u nobody test -r . -a -x .; then + error "Directory '%s' must be readable by user 'nobody'" "$dir" + return 1 + fi + return 0 +) + # Usage: exit_copy $copydir $src_owner # End immediately, but copy log files out exit_copy() { @@ -62,17 +84,18 @@ exit_copy() { run_hook() { local hookname=$1; shift local hookvar="hook_${hookname}[@]" + local fails=() - msg "Running hook: %s" "$hookname" for hook in "${!hookvar}"; do - msg2 'hook: %s' "$hook" - "$hook" "$@" || { error "result: %s" $?; fails+=("$hook"); } - done + "$hook" "$@" || fails+=("$hook") + done |& indent + if [[ ${#fails[@]} -gt 0 ]]; then error "Failure(s) in %s: %s" "$hookname" "${fails[*]}" return 1 + else + return 0 fi - return 0 } # Usage: add_to_local_repo $copydir $pkgfiles... @@ -88,25 +111,32 @@ add_to_local_repo() { done } +hook_post_build+=('cleanup') +cleanup() { + local copydir=$1 + rm -f -- "$copydir"/chroot{prepare,build} +} + build() ( local copydir=$1; shift - local cmd=(/chrootbuild "$@") + local repack=$1; shift - run_hook pre_build "$copydir" - trap "run_hook post_build '$copydir'" EXIT - - local netflag='' + local run_ynet=() + local run_nnet=() if $INCHROOT; then - ! $NONET || netflag='-n' - unshare $netflag -- "${cmd[@]}" + run_ynet=(unshare) + run_nnet=(unshare -n) else - ! $NONET || netflag='-N' - librechroot $netflag \ - -r "$PWD:/startdir_host" \ - -r "$SRCDEST:/srcdest_host" \ - -l "$copydir" \ - run "${cmd[@]}" + run_ynet=(librechroot "${librechroot_flags[@]}" run) + run_nnet=(librechroot "${librechroot_flags[@]}" -N run) fi + $NONET || run_nnet=("${run_ynet[@]}") + + prepare_chroot "$copydir" "$LIBREHOME" "$repack" false + "${run_ynet[@]}" /chrootprepare false "$@" |& indent + run_hook pre_build "$copydir" + trap "run_hook post_build '$copydir'" EXIT + "${run_nnet[@]}" /chrootbuild false "$@" |& indent ) # The main program ############################################################# @@ -118,7 +148,7 @@ usage() { prose 'If run from outside of a chroot, command will make the following configuration changes in the chroot:' bullet 'whatever changes `librechroot` makes.' - bullet 'set `PKGDEST` and `SRCDEST` in `/etc/makepkg.conf`' + bullet 'set `{PKG,SRC,SRCPKG,LOG}DEST` in `/etc/makepkg.conf`' bullet 'set `PACKAGER` in `/etc/makepkg.conf` to reflect the value outside of the chroot.' bullet '(maybe) delete `/build/.makepkg.conf`' @@ -134,8 +164,12 @@ usage() { the documentation there.' echo print 'Options:' + print ' %s options:' librechroot flag "-n <$(_ CHROOT)>" 'Name of the chroot to use' flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the chroot copy to use' + flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write' + flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only' + print ' %s options:' libremakepkg flag '-N' "Don't disable networking during build() and package(). PLEASE don't use this unless you have a special reason, its use is a violation @@ -159,10 +193,14 @@ main() { local chroot='' # Parse command line options ########################################### - while getopts 'n:l:NRh' flag ; do + while getopts 'n:l:w:r:NRh' flag ; do case "${flag}" in - n) if $INCHROOT; then err_chflag "$flag"; else chroot=$OPTARG; fi;; - l) if $INCHROOT; then err_chflag "$flag"; else copy=$OPTARG; fi;; + n) if $INCHROOT; then err_chflag "$flag"; else + chroot=$OPTARG; fi;; + l) if $INCHROOT; then err_chflag "$flag"; else + copy=$OPTARG; fi;; + w|r) if $INCHROOT; then err_chflag "$flag"; else + librechroot_flags+=(-$flag "$OPTARG"); fi;; N) NONET=false;; R) repack=true; makepkg_args+=(-R);; h) usage; return 0;; @@ -190,6 +228,15 @@ main() { fi unset chroot + # Load makepkg configuration ########################################### + # Note that all of these are globals + PKGDEST="$(get_var makepkg PKGDEST "$PWD")" + SRCDEST="$(get_var makepkg SRCDEST "$PWD")" + SRCPKGDEST="$(get_var makepkg SRCPKGDEST "$PWD")" + LOGDEST="$(get_var makepkg LOGDEST "$PWD")" + MAKEFLAGS="$(get_var makepkg MAKEFLAGS '')" + PACKAGER="$(get_var makepkg PACKAGER '')" + # Quick sanity check ################################################### if (( EUID )); then @@ -203,14 +250,17 @@ main() { exit 1 fi - # Load makepkg configuration ########################################### - # Note that all of these are globals - SRCDEST="$(get_conf_makepkg SRCDEST "$PWD")" - PKGDEST="$(get_conf_makepkg PKGDEST "$PWD")" - LOGDEST="$(get_conf_makepkg LOGDEST "$PWD")" - mkdir -p "$SRCDEST" "$PKGDEST" "$LOGDEST" - MAKEFLAGS="$(get_conf_makepkg MAKEFLAGS '')" - PACKAGER="$(get_conf_makepkg PACKAGER '')" + # Make sure that the various *DEST directories exist + mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST" + # Check the permissions for $startdir and $SRCDEST + ( + declare -i ret=0 + check_directory_permissions "$PWD" || ret=1 + if ! [[ "$PWD" -ef "$SRCDEST" ]]; then + check_directory_permissions "$SRCDEST" || ret=1 + fi + exit $ret + ) # OK, we are starting now ############################################## @@ -218,32 +268,40 @@ main() { lock 9 "/build/.lock" \ "Waiting for existing lock on build directory to be released" else + librechroot_flags+=( + -r "$PWD:/startdir_host" + -r "$SRCDEST:/srcdest_host" + -n "$CHROOT" + -l "$copy" + ) + # Obtain a lock on the chroot lock 9 "$copydir.lock" \ "Waiting for existing lock on chroot copy to be released: [%s]" "$copy" # Create the chroot if it does not exist - librechroot -n "$CHROOT" -l "$copy" make + msg 'Initializing the chroot...' + librechroot "${librechroot_flags[@]}" make |& indent fi # Set target CARCH # note that we waited until after locking/creating the chroot to do this - export CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_conf_makepkg CARCH)" + export CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)" # Pre-build + msg 'Starting pre-build activities...' run_hook check_pkgbuild - download_sources "$copydir" "$LIBREUSER" - prepare_chroot "$copydir" "$LIBREHOME" "$repack" false - clean_chroot "$copydir" + msg 'Downloading sources...' + download_sources "$copydir" "$LIBREUSER" |& indent # Build + msg 'Starting to build the package...' trap "exit_copy '$copydir' '$LIBREUSER'" EXIT - warning 'Entering build...' - build "$copydir" "${makepkg_args[@]}" + build "$copydir" "$repack" "${makepkg_args[@]}" + # Post-build - warning 'Entering hook check_pkg...' + msg 'Starting post-build activities...' run_hook check_pkg - warning 'Entering add_to_local_repo ...' - add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* + add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* |& indent } main "$@" diff --git a/src/chroot-tools/makechrootpkg.sh.patch b/src/chroot-tools/makechrootpkg.sh.patch index f5b8ed7..540e6ba 100644 --- a/src/chroot-tools/makechrootpkg.sh.patch +++ b/src/chroot-tools/makechrootpkg.sh.patch @@ -1,5 +1,5 @@ ---- makechrootpkg.sh.in 2013-09-08 23:01:20.000000000 -0400 -+++ makechrootpkg.sh.ugly 2013-09-09 15:43:06.000000000 -0400 +--- makechrootpkg.sh.in 2014-01-05 18:51:41.463720929 -0500 ++++ makechrootpkg.sh.ugly 2014-02-09 20:20:25.021630727 -0500 @@ -12,6 +12,7 @@ shopt -s nullglob @@ -8,8 +8,8 @@ _makepkg_args=(-s --noconfirm -L --holdver) makepkg_args=("${_makepkg_args[@]}") repack=false -@@ -26,9 +27,10 @@ - declare -i ret=0 +@@ -29,9 +30,10 @@ + bindmounts_rw=() copy=$USER -[[ -n $SUDO_USER ]] && copy=$SUDO_USER @@ -20,15 +20,15 @@ usage() { echo "Usage: ${0##*/} [options] -r <chrootdir> [--] [makepkg args]" -@@ -62,6 +64,7 @@ +@@ -67,6 +69,7 @@ exit 1 } +parse_options_init() { - while getopts 'hcur:I:l:nT' arg; do + while getopts 'hcur:I:l:nTD:d:' arg; do case "$arg" in h) usage ;; -@@ -86,9 +89,6 @@ +@@ -93,9 +96,6 @@ [[ ! -d $chrootdir ]] && die "No chroot dir defined, or invalid path '%s'" "$passeddir" [[ ! -d $chrootdir/root ]] && die "Missing chroot dir root directory. Try using: mkarchroot %s/root base-devel" "$chrootdir" @@ -38,9 +38,9 @@ if [[ ${copy:0:1} = / ]]; then copydir=$copy else -@@ -103,30 +103,47 @@ - repack=true - fi +@@ -115,30 +115,48 @@ + esac + done -if [[ -n $SUDO_USER ]]; then +if [[ -n ${SUDO_USER:-} ]]; then @@ -54,8 +54,9 @@ +# Usage: load_vars $makepkg_conf +# Globals: +# - SRCDEST -+# - LOGDEST ++# - SRCPKGDEST +# - PKGDEST ++# - LOGDEST +# - MAKEFLAGS +# - PACKAGER load_vars() { @@ -63,7 +64,7 @@ [[ -f $makepkg_conf ]] || return 1 - for var in {SRC,PKG,LOG}DEST MAKEFLAGS PACKAGER; do + for var in {SRC,SRCPKG,PKG,LOG}DEST MAKEFLAGS PACKAGER; do - [[ -z ${!var} ]] && eval $(grep "^${var}=" "$makepkg_conf") + [[ -z ${!var:-} ]] && eval $(grep "^${var}=" "$makepkg_conf") done @@ -92,7 +93,7 @@ # Get a read lock on the root chroot to make # sure we don't clone a half-updated chroot slock 8 "$chrootdir/root.lock" "Locking clean chroot" -@@ -147,10 +164,15 @@ +@@ -159,11 +177,16 @@ # Drop the read lock again lock_close 8 @@ -100,6 +101,7 @@ } -clean_temporary() { +- stat_busy "Removing temporary copy [%s]" "$copy" +# Usage: delete_chroot $copydir [$copy] +delete_chroot() { + local copydir=$1 @@ -107,10 +109,11 @@ + # Detect chrootdir filesystem type + local chroottype=$(stat -f -c %T "$copydir") + - stat_busy "Removing temporary copy [%s]" "$copy" - if [[ "$chroottype" == btrfs ]]; then ++ stat_busy "Removing chroot copy [%s]" "$copy" + if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then btrfs subvolume delete "$copydir" >/dev/null || -@@ -166,9 +188,14 @@ + die "Unable to delete subvolume %s" "$copydir" +@@ -178,9 +201,14 @@ stat_done } @@ -125,7 +128,7 @@ for install_pkg in "${install_pkgs[@]}"; do pkgname="${install_pkg##*/}" cp "$install_pkg" "$copydir/$pkgname" -@@ -179,11 +206,19 @@ +@@ -193,11 +221,19 @@ rm "$copydir/$pkgname" done @@ -147,9 +150,9 @@ $repack || rm -rf "$copydir/build" mkdir -p "$copydir/build" -@@ -217,12 +252,12 @@ +@@ -236,12 +272,12 @@ - chown -R nobody "$copydir"/{build,pkgdest,logdest,srcdest,startdir} + chown -R nobody "$copydir"/{build,pkgdest,srcpkgdest,logdest,srcdest,startdir} - if [[ -n $MAKEFLAGS ]]; then + if [[ -n ${MAKEFLAGS:-} ]]; then @@ -162,22 +165,26 @@ sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf" echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf" fi -@@ -235,6 +270,14 @@ +@@ -254,20 +290,38 @@ chmod 440 "$copydir/etc/sudoers.d/nobody-pacman" fi + if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then -+ cat >> "$copydir/etc/pacman.conf" <<EOF -+[repo] ++ local line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q') ++ local ins='[repo] +SigLevel = Optional TrustAll +Server = file:///repo -+EOF ++' ++ sed -i "${line}i${ins//$'\n'/\n}" "$copydir/etc/pacman.conf" + fi + # This is a little gross, but this way the script is recreated every time in the # working copy ++ printf $'#!/bin/bash\n%s\n_chrootprepare "$@"' "$(declare -f _chrootprepare)" \ ++ > "$copydir/chrootprepare" ++ chmod +x "$copydir/chrootprepare" printf $'#!/bin/bash\n%s\n_chrootbuild %q "$@"' "$(declare -f _chrootbuild)" \ -@@ -242,13 +285,19 @@ + "$run_namcap" >"$copydir/chrootbuild" chmod +x "$copydir/chrootbuild" } @@ -199,7 +206,7 @@ makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o else ( export SRCDEST BUILDDIR="$builddir" -@@ -258,7 +307,7 @@ +@@ -277,10 +331,10 @@ (( $? != 0 )) && die "Could not download sources." # Clean up garbage from verifysource @@ -207,8 +214,20 @@ + rm -rf "$builddir" } - _chrootbuild() { -@@ -295,6 +344,7 @@ +-_chrootbuild() { ++_chrootprepare() { + # This function isn't run in makechrootpkg, + # so no global variables + local run_namcap="$1"; shift +@@ -291,6 +345,7 @@ + shopt -s nullglob + + # XXX: Workaround makepkg disliking read-only dirs ++ rm -rf -- /srcdest/* /startdir/* + ln -sft /srcdest /srcdest_host/* + ln -sft /startdir /startdir_host/* + +@@ -316,11 +371,29 @@ # Safety check if [[ ! -w PKGBUILD ]]; then @@ -216,7 +235,30 @@ echo "Can't write to PKGBUILD!" exit 1 fi -@@ -312,12 +362,24 @@ + +- sudo -u nobody makepkg "${makepkg_args[@]}" || exit 1 ++ # Sync deps now, as networking may be disabled during _chrootbuild ++ cp /repo/repo.db /var/lib/pacman/sync/repo.db ++ sudo -u nobody makepkg "${makepkg_args[@]}" -o ++} ++ ++_chrootbuild() { ++ # This function isn't run in makechrootpkg, ++ # so no global variables ++ local run_namcap="$1"; shift ++ local makepkg_args=("$@") ++ ++ . /etc/profile ++ export HOME=/build ++ shopt -s nullglob ++ ++ cd /startdir ++ ++ sudo -u nobody makepkg "${makepkg_args[@]}" -e || exit 1 + + if $run_namcap; then + pacman -S --needed --noconfirm namcap +@@ -333,12 +406,24 @@ exit 0 } @@ -239,9 +281,9 @@ + local l for l in "$copydir"/logdest/*; do + [[ $l == */logpipe.* ]] && continue chown "$src_owner" "$l" - mv "$l" "$LOGDEST" -@@ -325,6 +387,10 @@ +@@ -352,6 +437,10 @@ } # }}} @@ -252,9 +294,9 @@ umask 0022 load_vars /etc/makepkg.conf -@@ -335,27 +401,37 @@ - [[ -d $SRCDEST ]] || SRCDEST=$PWD - [[ -d $LOGDEST ]] || LOGDEST=$PWD +@@ -363,30 +452,45 @@ + [[ -d $SRCPKGDEST ]] || SRCPKGDEST=$PWD + [[ -d $LOGDEST ]] || LOGDEST=$PWD -create_chroot +# Lock the chroot we want to use. We'll keep this lock until we exit. @@ -264,7 +306,9 @@ + sync_chroot "$chrootdir" "$copy" +fi - $update_first && arch-nspawn "$copydir" pacman -Syu --noconfirm + $update_first && arch-nspawn "$copydir" \ + "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ + pacman -Syu --noconfirm -[[ -n ${install_pkgs[*]} ]] && install_packages +if [[ -n ${install_pkgs[*]:-} ]]; then @@ -283,6 +327,12 @@ if arch-nspawn "$copydir" \ --bind-ro="$PWD:/startdir_host" \ --bind-ro="$SRCDEST:/srcdest_host" \ + "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ ++ /chrootprepare && ++ arch-nspawn "$copydir" \ ++ --bind-ro="$PWD:/startdir_host" \ ++ --bind-ro="$SRCDEST:/srcdest_host" \ ++ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ /chrootbuild "${makepkg_args[@]}" then - move_products @@ -296,7 +346,7 @@ if (( ret != 0 )); then if $temp_chroot; then -@@ -366,3 +442,4 @@ +@@ -397,3 +501,4 @@ else true fi diff --git a/src/fullpkg/Makefile b/src/fullpkg/Makefile index 2c76089..ee9c395 100644 --- a/src/fullpkg/Makefile +++ b/src/fullpkg/Makefile @@ -1 +1,3 @@ include ../../common.mk + +fullpkg-build.pot: xgettext-keywords-sh+=--keyword=list_pkgs:2 diff --git a/src/fullpkg/fullpkg b/src/fullpkg/fullpkg index 1d4388c..978cf7b 100755 --- a/src/fullpkg/fullpkg +++ b/src/fullpkg/fullpkg @@ -4,31 +4,32 @@ # aren't built or need update and then makepkg them in order. usage() { - - echo "cd to a dir containing a PKGBUILD and run:" - echo "$0 [build_dir]" - echo "" - echo "This script will check dependencies, build them if possible " - echo "and stage the packages on it's repo." - echo "" - echo "OPTIONS:" - echo " -h : this message." - echo "" - echo "Wrapper for \`fullpkg-find' and \`fullpkg-build'" - echo "" - exit 1 - + print "Usage: %s [OPTIONS] [BUILD_DIR]" "${0##*/}" + print "A (libre)makepkg wrapper that will also build dependencies" + echo + prose "More directly, it wraps fullpkg-find and fullpkg-build; + fullpkg-find finds the dependencies, and sets them up in + BUILD_DIR; then fullpkg-build builds them." + echo + prose "This script will check dependencies, build them if possible and + stage the packages on its repo." + echo + prose "If no BUILD_DIR is specified, it will create a temporary + directory." + echo + print "Options:" + flag '-h' "Show this message" } while getopts 'h' arg; do - case "$arg" in - h) usage ;; - esac + case "$arg" in + h) usage; exit 0 ;; + esac done shift $(( OPTIND - 1 )) -build_dir="${1:-$(mktemp -d /tmp/fullpkg.XXXXXX)}" +build_dir="${1:-$(mktemp --tmpdir -d fullpkg.XXXXXXXXXX)}" fullpkg-find "$build_dir" && fullpkg-build -N "$build_dir" exit 0 diff --git a/src/fullpkg/fullpkg-build b/src/fullpkg/fullpkg-build index 1771d83..e824c2e 100755 --- a/src/fullpkg/fullpkg-build +++ b/src/fullpkg/fullpkg-build @@ -18,101 +18,98 @@ check_vars libretools FULLBUILDCMD || exit 1 # ## status: nonfree, built, failed, unstaged list_pkgs() { - msg="$2" - local pkgs=($(grep "$1:" $build_dir/log)) && { - msg "$2" - echo ${pkgs[@]} | tr " " "\n" | cut -d: -f2 - } + msg="$2" + local pkgs=($(grep "$1:" $build_dir/log)) && { + msg "$2" + printf '%s\n' "${pkgs[@]}" | cut -d: -f2 + } } ## Check all build_dir, fails if one PKGBUILD is nonfree check_nonfree() { - find "$build_dir" -name PKGBUILD \ - -exec pkgbuild-check-nonfree {} + - if [ "$?" -eq 15 ]; then - error "Some PKGBUILD have nonfree problems" - exit 15 - fi - + find "$build_dir" -name PKGBUILD \ + -exec pkgbuild-check-nonfree {} + + local s=$? + pkgbuild-summarize-nonfree -q $s || return $s } # Removes a package from the buildorder # $1 package name # $2 buildorder file remove_buildorder() { - grep -Evw "${1}" ${2} > ${2}2 - mv -f ${2}2 ${2} + grep -Evw "${1}" ${2} > ${2}2 + mv -f ${2}2 ${2} - return $? + return $? } succesfull_build() { - if [ "$RUN" != "$FULLBUILDCMD" ]; then - return 0 # Custom command or download sources - fi + if [[ "$RUN" != "$FULLBUILDCMD" ]]; then + return 0 # Custom command or download sources + fi - if source .INFO && [ -n "$repo" ]; then + if source .INFO && [[ -n "$repo" ]]; then - if [ ! -z "$HOOKLOCALRELEASE" ]; then - "$HOOKLOCALRELEASE" "$repo" - fi + if [[ ! -z "$HOOKLOCALRELEASE" ]]; then + "$HOOKLOCALRELEASE" "$repo" + fi - msg "Updating pacman db and packages" - sudo pacman -Sy || true + msg "Updating pacman db and packages" + sudo pacman -Sy || true - fi + fi - echo "built:$(basename $PWD)" >>$build_dir/log + echo "built:$(basename $PWD)" >>$build_dir/log } build_description() { - list_pkgs "nonfree" "Those packages contain nonfree deps:" - list_pkgs "built" "Those packages were built and staged:" - list_pkgs "failed" "Those packages failed to build:" - list_pkgs "unstaged" "Those packages couldn't be staged (missing reponame):" + list_pkgs "nonfree" "Those packages contain nonfree deps:" + list_pkgs "built" "Those packages were built and staged:" + list_pkgs "failed" "Those packages failed to build:" + list_pkgs "unstaged" "Those packages couldn't be staged (missing reponame):" } __build() { - pushd ${build_dir} >/dev/null + pushd ${build_dir} >/dev/null - build_packages=($(sort -gr $buildorder | cut -d: -f2)) # greater levels must be built first + build_packages=($(sort -gr $buildorder | cut -d: -f2)) # greater levels must be built first - while [ ${#build_packages[@]} -ge 1 ]; do + while [[ ${#build_packages[@]} -ge 1 ]]; do - pushd "$build_dir/${build_packages[0]}" >/dev/null + pushd "$build_dir/${build_packages[0]}" >/dev/null - if [ -n "${HOOKPKGBUILDMOD}" ]; then - ${HOOKPKGBUILDMOD} || true - fi + if [[ -n "${HOOKPKGBUILDMOD}" ]]; then + ${HOOKPKGBUILDMOD} || true + fi - eval "$RUN"; r=$? + eval "$RUN"; r=$? - case $r in + case $r in - 0) succesfull_build ;; + 0) succesfull_build ;; - *) error "There were errors while trying to build the package." - echo "failed:$(basename $PWD)" >>$build_dir/log - ;; - esac + *) error "There were errors while trying to build the package." + echo "failed:$(basename $PWD)" >>$build_dir/log + ;; + esac - remove_buildorder "${build_packages[0]}" $buildorder || true + remove_buildorder "${build_packages[0]}" $buildorder || true -# which is next package? - build_packages=($(sort -gr $buildorder | cut -d: -f2)) - popd > /dev/null - done + # which is next package? + build_packages=($(sort -gr $buildorder | cut -d: -f2)) + popd > /dev/null + done - popd >/dev/null + popd >/dev/null } # End inmediately but print a useful message trap_exit() { - error "$@" - warning "Leftover files left on $build_dir" - mv .BUILDORDER BUILDORDER - exit 1 + error "$@" + warning "Leftover files left on $build_dir" + mv .BUILDORDER BUILDORDER + exit 1 } # Trap signals from makepkg @@ -124,66 +121,63 @@ trap 'trap_exit "(fullpkg-build) An unknown error has occurred. Exiting..."' ERR CLEANUP="false" CHECKNONFREE="true" RUN="$FULLBUILDCMD" -MESSAGE="Building packages" +MESSAGE="$(_ 'Building packages')" usage() { - - echo "" - echo "$(basename $0) [options] <build_dir>" - echo "" - echo "Builds packages from build_dir, create a build_dir using:" - echo "'fullpkg-find <build_dir>'" - echo "" - echo "If no <build_dir> is specified, it uses the current directory." - echo "" - echo "OPTIONS:" - echo " -h : this message." - echo " -c : clean <build_dir> on succesfull build" - echo " -N : don't check for freedom issues." #Also made by fullpkg-find - echo " -r \"command\" : use this instead of \"$FULLBUILDCMD\"." - echo " -g : get sources for building packages on build_dir." - echo "" - exit 1 - + print "Usage: %s [OPTIONS] [BUILD_DIR]" "${0##*/}" + print "Builds packages in BUILD_DIR, as set up by fullpkg-find" + echo + prose "Builds packages from BUILD_DIR; create BUILD_DIR using:" + print ' $ fullpkg-find BUILD_DIR' + echo + prose "If no BUILD_DIR is specified, it uses the current directory." + echo + print "Options:" + flag "-c" "Clean BUILD_DIR on succesfull build" + flag "-N" "Don't check for freedom issues." #Also made by fullpkg-find + flag "-r $(_ CMD)" "Use CMD instead of \${FULLBUILDCMD}" + flag "-g" "Get sources for building packages on BUILD_DIR" + flag "-h" "Show this message" } while getopts 'hNr:g' arg; do - case $arg in - h) usage ;; - c) CLEAN ;; - N) CHECKNONFREE="false" ;; - r) RUN="$OPTARG" - MESSAGE="Executing custom action";; - g) RUN='makepkg -g > /dev/null' - MESSAGE="Downloading packages";; - esac + case $arg in + h) usage; exit 0 ;; + c) CLEAN ;; + N) CHECKNONFREE="false" ;; + r) RUN="$OPTARG" + MESSAGE="$(_ 'Executing custom action')";; + g) RUN='makepkg -g > /dev/null' + MESSAGE="$(_ 'Downloading packages')";; + esac done shift $(( OPTIND - 1 )) build_dir="${1:-`pwd`}" buildorder="${build_dir}/BUILDORDER" -if [ ! -e "$buildorder" ]; then - error "This is not a build_dir. Make one using fullpkg." - usage +if [[ ! -e "$buildorder" ]]; then + error "This is not a build_dir. Make one using fullpkg." + usage + exit 1 else -# backup BUILDORDER - cp "$buildorder" "$build_dir/.BUILDORDER" + # backup BUILDORDER + cp "$buildorder" "$build_dir/.BUILDORDER" fi if "$CHECKNONFREE"; then - check_nonfree + check_nonfree fi msg "$MESSAGE" __build -if [ "$RUN" != "$FULLBUILDCMD" ]; then - # Used for downloading or custom command - mv "$build_dir/.BUILDORDER" "$buildorder" - exit 0 +if [[ "$RUN" != "$FULLBUILDCMD" ]]; then + # Used for downloading or custom command + mv "$build_dir/.BUILDORDER" "$buildorder" + exit 0 elif "$CLEANUP"; then - find "$build_dir" -mindepth 1 -delete + find "$build_dir" -mindepth 1 -delete fi build_description diff --git a/src/fullpkg/fullpkg-find b/src/fullpkg/fullpkg-find index a927782..8bd630c 100755 --- a/src/fullpkg/fullpkg-find +++ b/src/fullpkg/fullpkg-find @@ -1,5 +1,48 @@ #!/usr/bin/env bash # set -x # uncomment for debug + +# Copyright (C) 2011-2012 Joshua I. Haase H. (xihh) <hahj87@gmail.com> +# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@kiwwwi.com.ar> +# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu> +# Copyright (C) 2012-2014 Luke Shumaker <LukeShu@sbcglobal.net> +# +# The copy_files() function is taken from makechrootpkg: +# Copyright (C) 2007 Armin Luntzer <armin@archlinuxppc.org> +# Copyright (C) 2007 Jason Chu <jchu@xentac.net> +# Copyright (C) 2007, 2011 Dan McGee <dan@archlinux.org> +# Copyright (C) 2007-2008 Travis Willard <travis@archlinux.org> +# Copyright (C) 2007-2009 Aaron Griffin <aaronmgriffin@gmail.com> +# Copyright (C) 2008 Simo Leone <simo@archlinux.org> +# Copyright (C) 2009 Biru Ionut <biru.ionut@gmail.com> +# Copyright (C) 2009 Biru Ionut <ionut@archlinux.ro> +# Copyright (C) 2009 Evangelos Foutras <foutrelis@gmail.com> +# Copyright (C) 2009 Francois Charette <francois@samarqand.localdomain> +# Copyright (C) 2009 Nezmer <Nezmer@allurelinux.org> +# Copyright (C) 2009 Ronald van Haren <pressh@gmail.com> +# Copyright (C) 2009-2011 Andrea Scarpino <andrea@archlinux.org> +# Copyright (C) 2009-2012 Allan McRae <allan@archlinux.org> +# Copyright (C) 2009-2012 Eric Bélanger <snowmaniscool@gmail.com> +# Copyright (C) 2009-2012 Pierre Schmitz <pierre@archlinux.de> +# Copyright (C) 2010 Byron Clark <byron@theclarkfamily.name> +# Copyright (C) 2011 Ionut Biru <ibiru@archlinux.org> +# Copyright (C) 2011 Lukas Fleischer <archlinux@cryptocrack.de> +# Copyright (C) 2011-2012 Florian Pritz <bluewind@xinu.at> +# Copyright (C) 2011-2013 Jan Alexander Steffens (heftig) <jan.steffens@gmail.com> +# Copyright (C) 2013 Sébastien Luttringer <seblu@seblu.net> +# +# Because of the code from makechrootpkg, this file is GPLv2, instead of GPLv3 +# like most of libretools. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + + # Builds packages from ABS recursively. It tries to find dependencies that # aren't built or need update and then makepkg them in order. @@ -8,114 +51,108 @@ # Get repo name. Asumes ${ABSROOT}/repo/package/PKGBUILD guess_repo() { - basename $(dirname $(pwd)) + basename "$(dirname "$(pwd)")" } # This function is stolen from makechrootpkg. -# That probably has some copyright/licensing implications. copy_files() { - local copydir="$build_dir/${pkgbase:-${pkgname[0]}}" - mkdir -p "$copydir" - - # Copy PKGBUILD and sources - cp PKGBUILD "$copydir" - ( - load_PKGBUILD - for file in "${source[@]}"; do - file="${file%%::*}" - file="${file##*://*/}" - if [[ -f $file ]]; then - cp "$file" "$copydir/" - elif [[ -f $SRCDEST/$file ]]; then - cp "$SRCDEST/$file" "$copydir/" - fi - done - - # Find all changelog and install files, even inside functions - for i in 'changelog' 'install'; do - while read -r file; do - # evaluate any bash variables used - eval file=\"$(sed 's/^\(['\''"]\)\(.*\)\1$/\2/' <<< "$file")\" - [[ -f $file ]] && cp "$file" "$copydir" - done < <(sed -n "s/^[[:space:]]*$i=//p" PKGBUILD) - done - ) + local copydir="$build_dir/${pkgbase:-${pkgname[0]}}" + mkdir -p "$copydir" + + # Copy PKGBUILD and sources + cp PKGBUILD "$copydir" + ( + load_PKGBUILD + for file in "${source[@]}"; do + file="${file%%::*}" + file="${file##*://*/}" + if [[ -f $file ]]; then + cp "$file" "$copydir/" + elif [[ -f $SRCDEST/$file ]]; then + cp "$SRCDEST/$file" "$copydir/" + fi + done + + # Find all changelog and install files, even inside functions + for i in 'changelog' 'install'; do + while read -r file; do + # evaluate any bash variables used + eval file=\"$(sed "s/^\\(['\"]\\)\\(.*\\)\\1\$/\\2/" <<< "$file")\" + [[ -f $file ]] && cp "$file" "$copydir" + done < <(sed -n "s/^[[:space:]]*$i=//p" PKGBUILD) + done + ) } # Checks ABSROOT and look for target pkg deps. Adds them if not built or outdated. find_deps() { -# Check this level - load_PKGBUILD - - local repo="${repo:-$(guess_repo)}" - local pkgbase="${pkgbase:-${pkgname[0]}}" - - if ! pkgbuild-check-nonfree > /dev/null 2> /dev/null; then - if [ "$?" -eq 15 ]; then - error "pkgbase" has nonfree issues - return 15 - fi - fi - - # Checking any package built, since otherwise e.g. kdebase would - # be always considered outdated: there is no package built named kdebase. - # TODO: maybe check for the package requested in case of recursive calls, - # instead of the first one listed? - if is_built "${pkgname[0]}" "$(get_full_version "${pkgname[0]}")"; then - exit 0 # pkg is built and updated - fi - -# greater levels are built first - echo "${LEVEL}:${pkgbase}" >>"$build_dir/BUILDORDER" -# PKGBUILD is already there - if [ -d "${build_dir}/${pkgbase}" ]; then - exit 0 -# Copy dir to build_dir - else - copy_files - -# to identify repo later - echo "repo=$repo" > "${build_dir}/${pkgbase}/.INFO" - fi - -# current package plus a space for every level - msg2 "%${LEVEL}s${pkgbase}-$(get_full_version)" - -## Check next levels - declare -i next_level=$LEVEL+1 - -# All deps in separate line, only once, without version. - deps=($(echo "${depends[@]} ${makedepends[@]}" | \ - sed "s/[=<>]\+[^ ]\+//g" | \ - tr ' ' "\n" | \ - sort -u)) - - for _dep in ${deps[@]}; do - - local found=false - # May fail, e.g. since abslibre-mips64el doesn't include - # arch=any packages. - local pkgdir=$(toru -p ${_dep}) || true - - if [ -n "$pkgdir" -a -d "${pkgdir}" ]; then - found=true - - pushd "${pkgdir}" > /dev/null -# runs itself on dep's PKGBUILD dir - $0 -l ${next_level} ${build_dir} || return $? - popd > /dev/null - fi - - if ! (( found )); then - echo "dep_not_found:$_dep" >>$build_dir/log - fi - - done - -## End variable block - - unset next_level dir + # Check this level + load_PKGBUILD + + local repo="${repo:-$(guess_repo)}" + local pkgbase="${pkgbase:-${pkgname[0]}}" + + # Checking any package built, since otherwise e.g. kdebase would + # be always considered outdated: there is no package built named kdebase. + # TODO: maybe check for the package requested in case of recursive calls, + # instead of the first one listed? + if is_built "${pkgname[0]}" "$(get_full_version "${pkgname[0]}")"; then + exit 0 # pkg is built and updated + fi + + # greater levels are built first + echo "${LEVEL}:${pkgbase}" >>"$build_dir/BUILDORDER" + # PKGBUILD is already there + if [[ -d "${build_dir}/${pkgbase}" ]]; then + exit 0 + # Copy dir to build_dir + else + copy_files + + # to identify repo later + echo "repo=$repo" > "${build_dir}/${pkgbase}/.INFO" + fi + + # current package plus a space for every level + msg2 "%${LEVEL}s%s" '' "${pkgbase}-$(get_full_version)" + + ## Check next levels + declare -i next_level=$LEVEL+1 + + # All deps + local deps=("${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}") + # Strip version specifiers + deps=("${deps[@]%%[=<>]*}") + # Filter out duplicates + read -d $'\n' -a deps <<<"$(printf '%s\n' "${deps[@]}"|sort -u)" + + local _dep + for _dep in "${deps[@]}"; do + + local found=false + # May fail, e.g. since abslibre-mips64el doesn't include + # arch=any packages. + local pkgdir=$(toru -p ${_dep}) || true + + if [[ -n "$pkgdir" ]] && [[ -d "${pkgdir}" ]; then + found=true + + pushd "${pkgdir}" > /dev/null + # runs itself on dep's PKGBUILD dir + "$0" -l "${next_level}" "${build_dir}" || return $? + popd > /dev/null + fi + + if ! (( found )); then + echo "dep_not_found:$_dep" >>$build_dir/log + fi + + done + + ## End variable block + + unset next_level dir } . libremessages @@ -128,68 +165,67 @@ CLEANFIRST='false' UPDATEDB='true' usage() { - - echo "" - echo "cd to a dir containing a PKGBUILD and run:" - echo "$(basename $0) [options] <build_dir>" - echo "" - echo "This script will create a build_dir for recursive building" - echo "it tries to find dependencies that aren't built or need update." - echo "" - echo "If no <build_dir> is specified, the script works on a tempdir" - echo "" - echo "OPTIONS:" - echo " -h : this message." - echo " -A <absroot> : use this ABSROOT." - echo " -c : clean <build_dir> before working." - echo " -m <max_level> : check deps until this level" - echo " -n : don't update pacman db." - echo "" - exit 1 - + print "Usage: %s [OPTIONS] [BUILD_DIR]" "${0##*/}" + print "Finds package dependencies and sets up a build order" + echo + prose "Run this from a directory containing a PKGBUILD." + echo + prose "This script will create a BUILD_DIR for recursive building; it + tries to find dependencies that aren't built or need update." + echo + prose "If no BUILD_DIR is specified, it will create a temporary + directory." + echo + print "Options:" + flag "-h" "Show this message" + flag "-A <$(_ ABSROOT)>" "Use ABSROOT as the root of the ABS tree" + flag "-c" "Clean BUILD_DIR before working." + flag "-m <$(_ MAX_LEVEL)>" "Limit the depth of the dependency recursion" + flag "-n" "Don't update pacman DB" } while getopts 'hA:l:cmn' arg; do - case "$arg" in - h) usage ;; - A) ABSROOT="$OPTARG" ;; - l) LEVEL="$OPTARG" ;; # hidden option to know dep level. - c) CLEANFIRST='true' ;; - m) MAXLEVEL="$OPTARG" ;; - n) UPDATEDB='false' ;; - esac + case "$arg" in + h) usage; exit 0 ;; + A) ABSROOT="$OPTARG" ;; + l) LEVEL="$OPTARG" ;; # hidden option to know dep level. + c) CLEANFIRST='true' ;; + m) MAXLEVEL="$OPTARG" ;; + n) UPDATEDB='false' ;; + esac done -if [ ! -r PKGBUILD ]; then - error "This directory doesnt contain a PKGBUILD" - usage +if [[ ! -r PKGBUILD ]]; then + error "This directory doesnt contain a PKGBUILD" + usage + exit 1 fi shift $(( OPTIND - 1 )) build_dir="${1}" -if [ "$LEVEL" -eq 0 ]; then +if [[ "$LEVEL" -eq 0 ]]; then - build_dir="${1:-$(mktemp -d /tmp/fullpkg.XXXXXX)}" + build_dir="${1:-$(mktemp -d /tmp/fullpkg.XXXXXX)}" - if [ ! -d "$build_dir" ]; then - mkdir -p "$build_dir" - elif "$CLEANFIRST"; then - # Erase files already in dir - msg "Cleaning up files in dir" - find "$build_dir" -mindepth 1 -delete - fi + if [[ ! -d "$build_dir" ]]; then + mkdir -p "$build_dir" + elif "$CLEANFIRST"; then + # Erase files already in dir + msg "Cleaning up files in dir" + find "$build_dir" -mindepth 1 -delete + fi - if "$UPDATEDB"; then - msg "Updating pacman db" - sudo pacman -Sy --noconfirm || true - fi + if "$UPDATEDB"; then + msg "Updating pacman db" + sudo pacman -Sy --noconfirm || true + fi -# make files for log and buildorder - touch "${build_dir}"/{log,BUILDORDER} - buildorder="${build_dir}/BUILDORDER" + # make files for log and buildorder + touch "${build_dir}"/{log,BUILDORDER} + buildorder="${build_dir}/BUILDORDER" - msg "Checking dependencies" + msg "Checking dependencies" fi find_deps diff --git a/src/libregit/Makefile b/src/gitget/Makefile index 2c76089..2c76089 100644 --- a/src/libregit/Makefile +++ b/src/gitget/Makefile diff --git a/src/gitget/gitget b/src/gitget/gitget new file mode 100755 index 0000000..3e35cec --- /dev/null +++ b/src/gitget/gitget @@ -0,0 +1,220 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2013 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. libremessages + +# from makepkg +dir_is_empty() { + ( + shopt -s dotglob nullglob + files=("$1"/*) + (( ${#files} == 0 )) + ) +} + +# from makepkg +cd_safe() { + if ! cd "$1"; then + error "Failed to change to directory %s" "$1" + plain "Aborting..." + exit 1 + fi +} + +# from makepkg +download_git_checkout() { + local url=$1 + local ref=$2 + local dir=$3 + local name=$4 + local push=${5:-} + + if [[ ! -d "$dir/.git" ]] ; then + msg2 "Cloning %s %s repo..." "${name}" "git" + if ! git clone "$url" "$dir"; then + error "Failure while downloading %s %s repo" "${name}" "git" + plain "Aborting..." + exit 1 + fi + cd_safe "$dir" + if [[ -n $push ]]; then + git config remote.origin.pushUrl "$push" + fi + git checkout "$ref" + else + cd_safe "$dir" + # Make sure we are fetching the right repo + if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then + if $FORCE; then + git config remote.origin.url "$url" + else + error "%s is not a clone of %s" "$dir" "$url" + plain "Aborting..." + exit 1 + fi + fi + if [[ -n $push ]] ; then + if $FORCE; then + git config remote.origin.pushUrl "$push" + else + local curpush="$(git config --get remote.origin.pushUrl)" + if [[ $? != 0 ]] ; then + error "%s does not have a %s configured" pushUrl "$name" + plain "Aborting..." + exit 1 + elif [[ $curpush != "$push" ]]; then + error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push" + plain "Aborting..." + exit 1 + fi + fi + fi + msg2 "Updating %s %s repo..." "${name}" "git" + if ! git pull origin "$ref"; then + # only warn on failure to allow offline builds + warning "Failure while updating %s %s repo" "${repo}" "git" + fi + fi +} + +# from makepkg +download_git_bare() { + local url=$1 + local dir=$2 + local name=$3 + local push=${4:-} + + if [[ ! -d "$dir" ]] || dir_is_empty "$dir" ; then + msg2 "Cloning %s %s repo..." "${name}" "git" + if ! git clone --mirror "$url" "$dir"; then + error "Failure while downloading %s %s repo" "${name}" "git" + plain "Aborting..." + exit 1 + fi + if [[ -n $push ]]; then + cd_safe "$dir" + git config remote.origin.pushUrl "$push" + fi + else + cd_safe "$dir" + # Make sure we are fetching the right repo + if [[ "$url" != "$(git config --get remote.origin.url)" ]] ; then + error "%s is not a clone of %s" "$dir" "$url" + plain "Aborting..." + exit 1 + fi + if [[ -n $push ]] ; then + if $FORCE; then + git config remote.origin.pushUrl "$push" + else + local curpush="$(git config --get remote.origin.pushUrl)" + if [[ $? != 0 ]] ; then + error "%s does not have a %s configured" pushUrl "$name" + plain "Aborting..." + exit 1 + elif [[ $curpush != "$push" ]]; then + error "%s has %s configured, but it doesn't match %s" "$name" pushUrl "$push" + plain "Aborting..." + exit 1 + fi + fi + fi + msg2 "Updating %s %s repo..." "${name}" "git" + if ! git fetch --all -p; then + # only warn on failure to allow offline builds + warning "Failure while updating %s %s repo" "${name}" "git" + fi + fi +} + +usage() { + print 'Usage: %s [OPTIONS] bare URL DIRECTORY' "${0##*/}" + print 'Usage: %s [OPTIONS] checkout URL DIRECTORY' "${0##*/}" + print 'A URL-handler for git urls. Capable of updating or cloning.' + echo + prose "Clones or pulls from the git URL, to a local DIRECTORY. If + \`bare\` is specified, it will create a bare repository; if + \`checkout\` is specified, it will create or update a working tree." + echo + prose 'For a checkout, the tree to checkout is specified by appending + `#ANYTHING=TREE-ISH` to the URL. For example, `#branch=stable`, + or `#tag=v12.3`. Whatever is on the left side of the equal sign + is ignored, this is for compatability with `makepkg` source + URLs.' + echo + prose "The URL may be prefixed with \`git+\`. This is also for + compatability with \`makepkg\` source URLs." + echo + prose "It does safety checks, figures out whether to clone or pull, and + other helpful things. This exists because the same + \`download_git\` function from makepkg was being copied and + pasted again and again." + echo + print "Options:" + flag '-f' \ + 'Instead of checking to make sure configured URLs match, force + the update, and set the URLs.' + flag "-p $(_ URL)" \ + 'In addition to setting or checking `remotes.origin.url`, also + set or check `remotes.origin.pushUrl`' + flag "-n $(_ NAME)" \ + 'In messages, instead of using the basename of DIRECTORY as the + repository name, use NAME' + flag '-h' 'Show this message' +} + +FORCE=false +main() { + local push='' + local name='' + while getopts 'fp:n:h' flag; do + case "${flag}" in + f) FORCE=true;; + p) push=$OPTARG;; + n) name=$OPTARG;; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + [[ $# == 3 ]] || { usage >&2; return 1; } + local mode=$1 + local url=${2#git+} + local dir=$3 + + local urlmain=${url%%#*} + local urlfrag=${url#*#} + if [[ "$urlfrag" == "$urlmain" ]]; then + urlfrag='' + fi + local ref=${urlfrag#*=} + + if [[ -z $ref ]]; then + ref=master + fi + + name=${name:-${dir##*/}} + + case "$mode" in + checkout) download_git_checkout "$urlmain" "$ref" "$dir" "$name" "$push";; + bare) download_git_bare "$urlmain" "$dir" "$name" "$push";; + *) usage >&2; return 1;; + esac +} + +main "$@" diff --git a/src/gitget/libregit b/src/gitget/libregit new file mode 100755 index 0000000..95b59ff --- /dev/null +++ b/src/gitget/libregit @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# Copyright (C) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. libremessages + +usage() { + print 'Usage: %s repo ref dir' "${0##*/}" + print 'A compatability wrapper around `gitget checkout`' + echo + prose "This exists because gitget used to be called libregit, and took + the arguments in this format, and I'm sure there are a few + scripts floating around that use it." + echo + prose "Clones or pulls from the git URL 'repo', and checks out the git + ref 'ref' to the directory 'dir'." +} + + +main() { + [[ $# == 3 ]] || { usage >&2; return 1; } + repo=$1 + ref=$2 + dir=$3 + + gitget checkout "${repo}#ref=${ref}" "${dir}" +} diff --git a/src/is_built b/src/is_built index 80d0719..ba5f061 100755 --- a/src/is_built +++ b/src/is_built @@ -1,18 +1,21 @@ #!/usr/bin/env bash -cmd=${0##*/} +. libremessages + usage() { - echo "Usage: $cmd [-h] pkgname [pkgver]" + print "Usage: %s [-h] PKGNAME [PKGVER]" "${0##*/}" "${0##*/}" + print 'Detect iv a given package (version) is already in the repos' echo - echo "Detect if a given package version is already in repos" - echo "Assuming you want greater or equal." + prose "If a version is specified, it assumedx that you want a greater or + equal version." echo - echo "Example usage: $cmd 'pcre' '20'" + prose "Example usage:" + print " $ %s 'pcre' '20'" "${0##*/}" echo - echo "Exit status:" - echo " 0: The package is built" - echo " 1: The package has not built" - echo " >1: There was an error" + print "Exit status:" + print " 0: The package is built" + print " 1: The package has not built" + print " >1: There was an error" } while getopts 'h' arg; do diff --git a/src/lib/Makefile b/src/lib/Makefile index 45fd330..495abb1 100644 --- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -1,8 +1,10 @@ copy_files = common.sh.in -libexecs = $(filter-out librelib,$(wildcard libre*)) +libexecs = $(filter-out librelib,$(progs)) # include common.sh in libs explicitly, because it might not exist yet # when the wildcard is performed libs = $(sort $(wildcard *.sh) common.sh) + +pots = $(libs) include ../../common.mk # Build ############################################################## @@ -12,19 +14,24 @@ common.sh: %: %.in %.top Makefile @{ \ cat '$*.top' && \ echo && \ - sed -r -e '/encoding problem/d;/LANG=/d' -e 's/mesg=\$$(.)/mesg="$$(_ "$$\1")"/' '$*.in' && \ + sed -r \ + -e '/encoding problem/d;/LANG=/d' \ + -e 's/mesg=\$$(.)/mesg="$$(_ "$$\1")"/' \ + -e 's/gettext /_l _ /g' \ + -e "s/^(\s+)(msg|error) '/\1_l \2 '/" \ + -e 's|lock\(\)\s*\{|lock()\n{|' \ + '$*.in' && \ echo && \ cat '$*.bottom' && \ :; } > '$@' # Translate ########################################################## -pot: libreblacklist.pot common.sh.pot librelib.pot - libreblacklist.pot: libreblacklist { \ - sed -n '/^# Usage:/,/()/{ /^#/ { =; p; } }' $< | sed -r -e 's/^# (.*)/msgid "\1"/' -e 's/^[0-9]*$$/#. embedded usage text\n#: $<:&/'; \ - sed -rn '/print /{ =; s/\s*print "([^"]*)".*/msgid "\1"/p; }' $< | sed 's/^[0-9]*$$/#. print\n#: $<:&/' ; \ - } | sed 's/^msgid .*/&\nmsgstr ""\n/' > $@ -common.sh.pot: common.sh - xgettext --omit-header -i --from-code=UTF-8 -L shell -o $@ $< + sed -n '/^# Usage:/,/()/{ /^#/ { =; p; } }' $< | \ + sed -r -e 's/^# (.*)/msgid "\1"\nmsgstr ""\n/' \ + -e 's/^[0-9]*$$/#. embedded usage text\n#: $<:&/'; \ + $(xgettext-sh-prose); \ + $(xgettext-sh-std); \ + } | $(pofmt) > $@ diff --git a/src/lib/blacklist.sh.3.ronn b/src/lib/blacklist.sh.3.ronn new file mode 120000 index 0000000..9001445 --- /dev/null +++ b/src/lib/blacklist.sh.3.ronn @@ -0,0 +1 @@ +libreblacklist.1.ronn
\ No newline at end of file diff --git a/src/lib/common.sh.3.ronn b/src/lib/common.sh.3.ronn new file mode 100644 index 0000000..2ec113c --- /dev/null +++ b/src/lib/common.sh.3.ronn @@ -0,0 +1,16 @@ +common.sh -- common Bash routines from devtools +=============================================== + +## SYNPOSIS + +`. $(librelib common)` + +## DESCRIPTION + +In short, don't use this. Use `libremessages`(1) instead. +`libremessages` uses this internally. + +## SEE ALSO + + * librelib(7) + * libremessages(1)/messages.sh(3) diff --git a/src/lib/common.sh.top b/src/lib/common.sh.top index 054301b..9c4ba2e 100644 --- a/src/lib/common.sh.top +++ b/src/lib/common.sh.top @@ -13,14 +13,20 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. +shopt -s extglob + if [[ -z ${_INCLUDE_COMMON_SH:-} ]]; then _INCLUDE_COMMON_SH=true -export TEXTDOMAIN='libretools' -export TEXTDOMAINDIR='/usr/share/locale' +[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools' +[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale' if type gettext &>/dev/null; then _() { gettext "$@"; } else _() { echo "$@"; } fi + +_l() { + TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@" +} diff --git a/src/lib/conf.sh b/src/lib/conf.sh index f96af26..ee52f6f 100644 --- a/src/lib/conf.sh +++ b/src/lib/conf.sh @@ -1,7 +1,7 @@ #!/bin/bash # non-executable, but put this there as a hint to text editors # This may be included with or without `set -euE` -# Copyright (c) 2012-2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# Copyright (C) 2012-2014 by Luke Shumaker <lukeshu@sbcglobal.net> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ if [[ -z ${XDG_CACHE_HOME:-} ]]; then export XDG_CACHE_HOME="${LIBREHOME}/.cache" fi -# Generic functions ############################################################ +# Low-level generic functions ################################################## # Usage: list_files $slug # Lists the configuration files to be considered for $slug. @@ -61,7 +61,7 @@ list_files() { } # Usage: list_envvars $slug -# Lists the environmental variables that take precidence over the configuration +# Lists the environmental variables that take precedence over the configuration # files for $slug. list_envvars() { local slug=$1 @@ -73,10 +73,16 @@ list_envvars() { PKGEXT SRCEXT \ GPGKEY PACKAGER ;; + libretools) + printf '%s\n' \ + DIFFPROG + ;; *) :;; esac } +# High-level generic functions ################################################# + # Usage: load_files $slug # Loads the configuration files for $slug in the proper order. load_files() { @@ -106,7 +112,7 @@ load_files() { # Check whether the variables listed are properly set. # If not, it prints a message saying to set them in the configuration file(s) # for $slug. -check_vars() { +check_vars() ( local slug=$1; shift local ret=0 @@ -116,10 +122,10 @@ check_vars() { if [[ -z ${!VAR:-} ]]; then type print &>/dev/null || . libremessages if [[ $(list_files $slug|wc -l) -gt 1 ]]; then - print "Configure '%s' in one of:" "$VAR" + _l print "Configure '%s' in one of:" "$VAR" list_files $slug | sed 's/./ -> &/' else - print "Configure '%s' in '%s'" "$VAR" "$(list_files $slug)" + _l print "Configure '%s' in '%s'" "$VAR" "$(list_files $slug)" fi ret=1 fi @@ -128,24 +134,27 @@ check_vars() { if [[ $ret != 0 ]]; then return 1 fi -} - -# makepkg configuration ######################################################## +) -# Usage: get_conf_makepkg <var_name> <default_value> -get_conf_makepkg() ( +# Usage: get_var <slug> <var_name> <default_value> +# Does not work with arrays +get_var() ( set +euE - local setting=$1 - local default=$2 - load_files makepkg - printf '%s\n' "${!setting:-${default}}" + local slug=$1 + local setting=$2 + local default=$3 + load_files "$slug" + printf '%s' "${!setting:-${default}}" ) -set_conf_makepkg() { - local key=$1 - local val=$2 +# Usage: set_var <slug> <var_name> <value> +# Does not work with arrays +set_var() { + local slug=$1 + local key=$2 + local val=$3 local file - for file in $(list_files makepkg|tac); do + for file in $(list_files "$slug"|tac); do if [[ -w $file ]]; then sed -i "/^\s*$key=/d" "$file" printf '%s=%q\n' "$key" "$val" >> "$file" @@ -185,6 +194,6 @@ unset_PKGBUILD() { load_PKGBUILD() { local file=${1:-./PKGBUILD} unset_PKGBUILD - CARCH="$(get_conf_makepkg CARCH "`uname -m`")" + CARCH="$(get_var makepkg CARCH "`uname -m`")" . "$file" } diff --git a/src/lib/conf.sh.3.ronn b/src/lib/conf.sh.3.ronn new file mode 100644 index 0000000..4108f9a --- /dev/null +++ b/src/lib/conf.sh.3.ronn @@ -0,0 +1,128 @@ +conf.sh(3) -- easy loading of configuration files +============================================== + +## SYNOPSIS + +`. $(librelib conf)` + +## DESCRIPTION + +`conf.sh` is a Bash(1) library to easily load various configuration +files related to Arch Linux/Parabola(7) and libretools(7). + +I recommend reading the source yourself--it is mostly +self-explanatory, and is shorter than this document. + +### VARIABLES + +When loading configuration files in a program run with `sudo`(8), it +is often desirable to load the configuration files from the home +directory of the user who called `sudo`, instead of from /root. + +To accommodate this, instead of using the usual $<USER> and $<HOME>, +`conf.sh` sets $<LIBREUSER> and $<LIBREHOME>, which it then uses. + + * <LIBREUSER>: + If $<SUDO_USER> is set, then $<LIBREUSER> is set to + that. Otherwise, $<LIBREUSER> is set to the value of $<USER>. + * <LIBREHOME>: + If $<LIBREUSER> == $<USER>, then $<LIBREHOME> is set to the value + of $<HOME>. Otherwise, it is set to the default home directory + of the user $<LIBREUSER>. + +Further, `conf.sh` works with XDG; it sets and uses +$<XDG_CONFIG_HOME> and $<XDG_CACHE_HOME> with the following values: + + * <XDG_CONFIG_HOME>: + If it isn't already set, it is set to "$<LIBREHOME>/.config" and + exported. + * <XDG_CACHE_HOME>: + If it isn't already set, it is set to "$<LIBREHOME>/.cache" and + exported. + +Note that only the XDG_* variables are exported. + +### GENERIC ROUTINES + +The following routines take a "slug" to refer to a group of +configuration files; that is the basename of the files. For example, +<SLUG>='abs' will identify `/etc/abs.conf` and `$<LIBREHOME>/.abs.conf`. + +The routines you will likely actually use are: + + * `load_files` <SLUG>: + Loads the configuration files for <SLUG>, loading the files in + the correct order, and checking the appropriate environmental + variables. + * `check_vars` <SLUG> <VARS>...: + Checks to see if all of <VARS> are defined. If any of them + aren't, it prints a message to configure them in the + configuration files for <SLUG>, and returns with a non-zero + status. + * `get_var` <SLUG> <VAR> <DEFAULT>: + If <VAR> is set in the configuration for <SLUG>, print it's + value, considering environmental variables. If it is not set, + return <DEFAULT>. This does NOT work for array variables. + * `set_var` <SLUG> <VAR> <VALUE>: + Set the variable <VAR> equal to <VALUE> in the configuration file + for <SLUG> of highest precedence that already exists, and is + writable. If no files fit this description, the routine does + nothing and returns a non-zero exit status. This does NOT work + for array variables. + +There are two more routines the above routines use internally that are +used internally by . You are unlikely to use them directly, but they +might be useful for debugging, or at least describing behavior. + + * `list_files` <SLUG>: + Lists (newline-separated) the configuration files that must be + considered for <SLUG>. Files listed later take precedence over + files listed earlier. + * `list_envvars` <SLUG>: + Lists (newline-separated) the environmental variables that take + precedence over the settings in the configuration files for + <SLUG>. For example, in `makepkg.conf`(8) (<SLUG>=makepkg), if the + <PACKAGER> environmental variable is set, the value in the + configuration file is ignored. + +### PKGBUILD ROUTINES + +These two routines deal with loading `PKGBUILD`(5) files. + + * `unset_PKGBUILD`: + Unsets all variables and functions that might be set in a + `PKGBUILD`(5) file, including those specific to `librefetch`(8). + * `load_PKGBUILD` [<FILE>]: + "Safely" loads a PKGBUILD. Everything that it would normally set + is unset first, $<CARCH> is set according to `makepkg.conf`(5), + then the file is loaded. The file to be loaded is <FILE>, or + "./PKGBUILD" by default. This isn't safe, security wise, in that + the PKGBUILD is free to execute code. + +### SLUGS + +The differences in behavior for anything that takes a slug comes down +to the differences in the output for `list_files` and `list_envvars`. + +The "known" slugs are "abs", "makepkg", and "libretools". If anything +else is given, then: + + * `list_files` will give back "/etc/libretools.d/<SLUG>.conf" and + "$<XDG_CONFIG_HOME>/libretools/<SLUG>.conf" + * `list_envvars` will give back an empty list. + +The rules for <SLUG>=(abs|makepkg|libretools) are more easily +expressed in code than prose, so it is recommended that you look at +that. + +## BUGS + +`get_var` and `set_var` do not work with arrays. + +## SEE ALSO + +librelib(7) + +abs.conf(5), makepkg.conf(5), libretools.conf(5), PKGBUILD(5) + +chroot.conf(5), librefetch.conf(5) diff --git a/src/lib/libreblacklist b/src/lib/libreblacklist index 5db1a3f..5525098 100755 --- a/src/lib/libreblacklist +++ b/src/lib/libreblacklist @@ -2,7 +2,7 @@ # This may be included with or without `set -euE` # When run directly, it does `set -euE` -# Copyright (c) 2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# Copyright (C) 2013-2014 by Luke Shumaker <lukeshu@sbcglobal.net> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -48,7 +48,7 @@ blacklist-update() ( local remote_blacklist="$BLACKLIST" local local_blacklist="$XDG_CACHE_HOME/libretools/blacklist.txt" - stat_busy "Downloading blacklist of proprietary software packages" + _l stat_busy "Downloading blacklist of proprietary software packages" mkdir -p "${local_blacklist%/*}" if wget -N -q -O "${local_blacklist}.part" "$remote_blacklist" 2>/dev/null; then @@ -58,9 +58,9 @@ blacklist-update() ( stat_done rm "${local_blacklist}.part" if [[ -e "$local_blacklist" ]]; then - warning "Using local copy of blacklist" + _l warning "Using local copy of blacklist" else - error "Download failed, exiting" + _l error "Download failed, exiting" return 1 fi @@ -109,6 +109,8 @@ if [[ "${0##*/}" == libreblacklist ]]; then sed 's/\r/\n/g'<<<"$1"|sed -e '/^$/d' -e 's/^# //' } usage() { + export TEXTDOMAIN='librelib' + export TEXTDOMAINDIR='/usr/share/locale' . $(librelib messages) if [[ $# -eq 0 ]]; then print "Usage: %s [-h] COMMAND [ARGUMENTS]" "${0##*/}" @@ -135,7 +137,7 @@ if [[ "${0##*/}" == libreblacklist ]]; then } if [[ $# -eq 0 ]]; then - usage >/dev/stderr + usage >&2 exit 1 fi _blacklist_cmd=$1 diff --git a/src/lib/libreblacklist.1.ronn b/src/lib/libreblacklist.1.ronn new file mode 100644 index 0000000..e44aa8d --- /dev/null +++ b/src/lib/libreblacklist.1.ronn @@ -0,0 +1,23 @@ +libreblacklist(1) -- Tools for working with the your-freedom blacklist +====================================================================== + +## SYNPOSIS + +`. $(librelib blacklist)`<br> +`. libreblacklist`<br> +`libremessages` [-h] <COMMAND> [<ARGS>...] + +## DESCRIPTION + +`libreblacklist` is a set of tools for working with the your-freedom +blacklist. + +It may be included into a `Bash`(1) program as a library, which will +expose it's routines as "blacklist-<COMMAND>", or it may be invoked on +the command line as "libreblacklist <COMMAND>". + +See `libremessages -h` for more information. + +## SEE ALSO + +librelib(7) diff --git a/src/lib/librelib b/src/lib/librelib index 2dc9314..d0a06e5 100755 --- a/src/lib/librelib +++ b/src/lib/librelib @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# Copyright (C) 2013-2014 by Luke Shumaker <lukeshu@sbcglobal.net> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,25 +16,39 @@ default_libdir=/usr/lib/libretools -if ! type gettext &>/dev/null; then - gettext() { echo "$@"; } +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } fi +_l() { + TEXTDOMAIN='librelib' TEXTDOMAINDIR='/usr/share/locale' "$@" +} + print() { - mesg=$1 + local mesg="$(_ "$1")" shift - printf -- "$(gettext "$mesg")\n" "$@" + printf -- "$mesg\n" "$@" +} + +_html_whitespace_collapse() { + [[ $# == 0 ]] || panic + tr '\n' ' ' | sed -r -e 's/\t/ /g' -e 's/ +/ /g' } prose() { - print "$@" | fmt -su + [[ $# -ge 1 ]] || panic + local mesg="$(_ "$(_html_whitespace_collapse <<<"$1")")"; shift + printf -- "$mesg" "$@" | fmt -u } cmd=${0##*/} usage() { . libremessages print 'Usage: . $(%s LIBRARY)' "$cmd" - print "Finds a shell library file" + print 'Usage: %s -h' "$cmd" + print "Finds a Bash library file" echo prose "While some libraries can be sourced just by their name because they are installed in PATH (like libremessages), some are not @@ -45,16 +59,19 @@ usage() { changed with the environmental variable LIBRETOOLS_LIBDIR.' "$default_libdir" echo print "Example usage:" - printf ' . $(%s conf.sh)\n' "$cmd" + printf ' . $(%s conf)\n' "$cmd" + echo + print "Options:" + flag '-h' 'Show this message' } main() { if [[ $# != 1 ]]; then - usage >&2 + _l usage >&2 return 2 fi if [[ $1 == '-h' ]]; then - usage + _l usage return 0; fi @@ -72,7 +89,7 @@ main() { return 0; fi done - print '%s: could not find library: %s' "$cmd" "$lib" >> /dev/stderr + _l print '%s: could not find library: %s' "$cmd" "$lib" >&2 return 1 } diff --git a/src/lib/librelib.1.ronn b/src/lib/librelib.1.ronn new file mode 100644 index 0000000..42d8f15 --- /dev/null +++ b/src/lib/librelib.1.ronn @@ -0,0 +1,55 @@ +librelib(1) -- finds a Bash library file +======================================== + +## SYNOPSIS + +`. $(librelib LIBRARY)`<br> +`librelib -h` + +## DESRIPTION + +`librelib` is a program to find a Bash(1) library file at run-time. +This way, the path does not need to be hard-coded into the +application; think of it as a sort of dynamic-linker for shell +programs. + +There are several reasons for doing this, instead of hard-coding the +path: + + * The install path can change in the future without having to change + programs that use them. + * The install directory can be configured at runtime, by setting + `LIBRETOOLS_LIBDIR`, similar to `LD_PRELOAD` (this is used when + running the test suite). + * The naming scheme of a library can change (such as between + `libreNAME` and `NAME.sh` without changing programs that use it. + +By default, `librelib` looks in `/usr/lib/libretools`, but that can be +changed by setting the `LIBRETOOLS_LIBDIR` environmental variable to +the directory it should look in. + +When searching for a library, `librelib` first strips `libre` from the +beginning of the name, and `.sh` from the end. This means that all of +the following are equivalent: + + . $(librelib messages) + . $(librelib messages.sh) + . $(librelib libremessages) + . $(librelib libremessages.sh) + +Once it has the 'base' name of the library it is looking for, it looks +for a file with that 'base' name (allowing for, but not requiring +`libre` to be prepended, or `.sh` to be appended) in whichever +directory it is looking in. + +If it cannot find a suitable library file, it will print an error +message to standard error, and exit with a code of 1. + +## Examples + + . $(librelib messages) + . $(librelib conf) + +## SEE ALSO + +librelib(7) diff --git a/src/lib/librelib.7.ronn b/src/lib/librelib.7.ronn new file mode 100644 index 0000000..e030f6a --- /dev/null +++ b/src/lib/librelib.7.ronn @@ -0,0 +1,50 @@ +librelib(7) -- Suite of Bash libraries +====================================== + +## SYNOPSIS + +Overview ot the librelib Bash library suite. + +## DESCRIPTION + +There are three parts to librelib: + + 1. The `librelib`(1) executable. + 2. The non-executable libraries installed in `/usr/lib/libretools` + 3. The executable libraries installed in both `/usr/bin` and + `/usr/lib/libretools`. + +The `librelib` executable isn't very exciting, it just finds the +libraries installed in `/usr/lib/libretools`. Think of it as a sort +of dynamic-linker. + +The 'core' of librelib are the libraries installed in +`/usr/lib/libretools`. These are `Bash`(1) libaries that may be sourced in +Bash programs. + +Some of these libraries also make sense as stand-alone programs, where +if they are invoked directly, the first argument is the library +routine to be executed. For example, the `messages` library may be +included, or executed: + + . $(librelib messages) + msg2 "Foo was found: %s" "$foo" + # or + libremessages msg2 "Foo was found: %s" "$foo" + +The `blacklist` library is similar: + + . $(librelib blacklist) + blacklist-update + # or + libreblacklist update + + + +## SEE ALSO + + * librelib(1) + * libremessages(1)/messages.sh(3) + * libreblacklist(1)/blacklist.sh(3) + * conf.sh(3) + * common.sh(3) diff --git a/src/lib/libremessages b/src/lib/libremessages index c6d08e2..e5b7157 100755 --- a/src/lib/libremessages +++ b/src/lib/libremessages @@ -2,15 +2,15 @@ # This may be included with or without `set -euE` # When run directly, it does `set -euE` -# Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> -# Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> -# Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> -# Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> -# Copyright (c) 2006 by Alex Smith <alex@alex-smith.me.uk> -# Copyright (c) 2006 by Andras Voroskoi <voroskoi@frugalware.org> -# Copyright (c) 2006 by Miklos Vajna <vmiklos@frugalware.org> -# Copyright (c) 2011 by Joshua Haase <hahj87@gmail.com> -# Copyright (c) 2012-2013 by Luke Shumaker <lukeshu@sbcglobal.net> +# Copyright (C) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> +# Copyright (C) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> +# Copyright (C) 2005 by Aurelien Foret <orelien@chez.com> +# Copyright (C) 2005 by Christian Hamar <krics@linuxforum.hu> +# Copyright (C) 2006 by Alex Smith <alex@alex-smith.me.uk> +# Copyright (C) 2006 by Andras Voroskoi <voroskoi@frugalware.org> +# Copyright (C) 2006 by Miklos Vajna <vmiklos@frugalware.org> +# Copyright (C) 2011 by Joshua Haase <hahj87@gmail.com> +# Copyright (C) 2012-2014 by Luke Shumaker <lukeshu@sbcglobal.net> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -36,7 +36,7 @@ ################################################################################ panic() { - echo "$(_ 'panic: malformed call to internal function')" >&2 + echo "$(_l _ 'panic: malformed call to internal function')" >&2 exit 1 } @@ -88,8 +88,6 @@ bullet() { # bullet. flag() { [[ $# == 2 ]] || panic - local n=' -' local flag=$1 local desc="$(_ "$(_html_whitespace_collapse <<<"$2")")" @@ -99,7 +97,7 @@ flag() { done local lines - IFS=$n lines=($(fmt -u -w $((73-indent)) <<<"$desc")) + IFS=$'\n' lines=($(fmt -u -w $((73-indent)) <<<"$desc")) local line for line in "${lines[@]}"; do printf " %-${indent}s %s\n" "$flag" "$line" @@ -119,6 +117,25 @@ term_title() { printf "$fmt" "$*" } +# Usage: setup_traps +# Sets up traps on TERM, HUP, QUIT and INT signals, as well as the ERR event, +# similar to makepkg +setup_traps() { + _libremessages_trap_exit() { + local signal=$1; shift + echo + error "$@" + trap -- "$signal" + kill "-$signal" "$$" + } + set -E + for signal in TERM HUP QUIT; do + trap "_libremessages_trap_exit $signal '%s signal caught. Exiting...' $signal" $signal + done + trap '_libremessages_trap_exit INT "Aborted by user! Exiting..."' INT + trap '_libremessages_trap_exit USR1 "An unknown error has occurred. Exiting..."' ERR +} + ################################################################################ # Run one of the defined functions if invoked directly # ################################################################################ diff --git a/src/lib/libremessages.1.ronn b/src/lib/libremessages.1.ronn new file mode 100644 index 0000000..d4c35fc --- /dev/null +++ b/src/lib/libremessages.1.ronn @@ -0,0 +1,220 @@ +libremessages(1) -- common Bash routines +======================================== + +## SYNOPSIS + +`. $(librelib messages)`<br> +`. libremessages`<br> +`libremessages` <COMMAND> + +## DESCRIPTION + +`libremessages` is a shell library containing many common routines. +The name is a bit of a misnomer, it mostly deals with printing +messages, but also has other things. + +`libremessages` uses `common.sh`(3) internally for a large portion of +it's functionality. The authors make no promises that functionality +that is implemented in `libremessages` won't move into `common.sh` or +vice-versa. So, it is recommended that you use `libremessages`, not +`common.sh`. + +### STAND ALONE USAGE + +The "normal" way to use libremessages is to source it, then call the +provided routines. + +However, if you call libremessages directly, the first argument is +taken as a the function to call, and the remaining arguments are +passed to it. The only cases where this doesn't work are the lockfile +routines (`lock`, `slock`, and `lock_close`), because lockfiles are +managed as file descriptors. + +### VARIABLES + +The following variables for printing terminal color codes are set: +`ALL_OFF`, `BOLD`, `BLUE`, `GREEN`, `RED`, `YELLOW`. If standard +error is not a terminal (see `isatty`(3)), they are set, but empty. +They are marked as readonly, so it is an error to try to set them +afterwords. + +### MESSAGE FORMAT + +All routines feed the message/format string through `gettext`(1), if +it is available. + +The descriptions will frequently reference `printf`(1)--this isn't +really that `printf`. The program described by the manual page +`printf`(1) is probably the version from GNU coreutils, every time it +is used here, it is `bash`(1)'s internal implementation; try running +the command `help printf` from a Bash shell for more information. + +### GENERAL ROUTINES + +Unless otherwise noted, these do not implicitly call `gettext`. + + * `_` <MESSAGE>: + If `gettext` is available, calls `gettext`, otherwise just prints + the arguments given. + + * `in_array` <NEEDLE> <HAYSTACK>...: + Evaluates whether <HAYSTACK> includes <NEEDLE>; returns 0 if it + does, non-zero if it doesn't. + + * `panic`: + For the times when you can't reasonably continue, similar to + "assert" in some programming languages. + + * `term_title` <MESSAGE>...: + Joins all arguments with whitespace, and sets the terminal title + to that. + + * `setup_traps`: + Sets traps on TERM, HUP, QUIT and INT signals, as sell as the ERR + event, similar to makepkg. + +### PROSE ROUTINES + +These routines print to standard output, ande are useful for printing +word-wrapped prose. + +For each of these, <MESSAGE> is fed through `gettext` automatically. + + * `print` <MESSAGE> [<ARGS>...]: + Like `printf`(1), but `gettext`-aware, and automatically prints a + trailing newline. + + * `prose` <MESSAGE> [<ARGS>...]: + Takes a `printf`(1)-formatted string, collapses whitespace + (HTML-style), and then word-wraps it. + + * `bullet` <MESSAGE> [<ARGS>...]: + Similar to `prose`, but prints a bullet point before the first + line, and indents the remaining lines. + + * `flag` <FLAG> <DESCRIPTION>: + Print a flag and description formatted for `--help` text. For + example:<br> + `flag '-N' 'Disable networking in the chroot'`<br> + The description is fed through `gettext`, the flag is not, so if + part of the flag needs to be translated, you must do that + yourself:<br> + `flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf'`<br> + Newlines in the description are ignored; it is + whitespace-collapsed (so newlines are stripped), then it is + re-word-wrapped, in the same way as `prose` and `bullet`. + +### NOTIFICATION ROUTINES + +These routines print to standard error, and all take arguments in the +same format as `printf`(1), except for `stat_done`, which doesn't take +any arguments. Each of these print to stderr, not stdout. + +For each of these, <MESSAGE> is fed through `gettext` automatically. + + * `plain` <MESSAGE> [<ARGS>...]: + Prints "plain" message in bold, indented with 4 spaces. + + * `msg` <MESSAGE> [<ARGS>...]: + Prints a top-level priority notification. + + * `msg2` <MESSAGE> [<ARGS>...]: + Prints a secondary notification. + + * `warning` <MESSAGE> [<ARGS>...]: + Prints a warning. + + * `error` <MESSAGE> [<ARGS>...]: + Prints an error message. + + * `stat_busy` <MESSAGE> [<ARGS>...]: + Prints a "working..." type message without a trailing newline. + + * `stat_done`: + Prints a "done" type message to terminate `stat_busy`. + +### TEMPORARY DIRECTORY MANAGEMENT + +These are used by devtools, and not used within the rest of +libretools. + +They work by creating and removing the directory referred to by the +variable $<WORKDIR>; `libretools.conf`(5) uses the same variable to +where the user saves all their work. If you aren't careful with +these, you could end up deleting a lot of someone's work. + + * `setup_workdir`: + Creates a temporary directory, and sets the environmental + variable $<WORKDIR> to it. Also sets traps for the signals INT, + QUIT, TERM and HUP to run `abort`; and EXIT to run `cleanup` + (see `signal`(7)). + + * `cleanup` [<EXIT_STATUS>]: + *If* `setup_workdir` has been run, `rm -rf "$WORKDIR"`. If given + a numeric argument, it will then call `exit`(1) with that argument. + + * `abort`: + Calls `msg` with the message "Aborting...", then calls + `cleanup 0`. + + * `die` <MESSAGE> [<ARGS>...]: + Exactly like `error`, but calls `cleanup` and calls `exit`(1) + with a status of 1. + +### LOCKFILE ROUTINES + + * `lock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]: + Opens (creating if nescessary) the file <LOCKFILE> with file + descriptor <FD> in the current process, and gets an exclusive + lock on it. If another program already has a lock on the file, + and this program needs to wait for the lock to be release, then + it uses `stat_busy`/`stat_done` to print <MESSAGE>. + + * `slock` <FD> <LOCKFILE> <MESSAGE> [<MSG_ARGS>...]: + Identical like `lock`, but opens a shared lock. This is also + known as a "read lock". Many programs can have a shared lock at + the same time, as long as no one has an exclusive lock on it. + + * `lock_close` <FD>: + Closes file descriptor <FD>, releasing the lock opened on it. + +### MAKEPKG ROUTINES + +These routines relate to `makepkg`(8). + + * `find_cached_package` <PKGNAME> <PKGVER>[-<PKGREL] <ARCH>: + Searches for a localy built copy of the specified package, in + <PKGDEST> and the current working directory. If <PKGREL> is not + specified, any value will match. If multiple matching files are + found (not counting duplicate links), then an error is printed to + stderr and nothing is prented to stdout. + + * `get_full_version` [<PKGNAME>]: + Inspects variables that are set, and prints the full version + spec, including <epoch> if necessary, <pkgver>, and <pkgrel>. By + default, it will print the information for <pkgbase>, following + the normal rules for finding <pkgbase>. If <PKGNAME> is given, + it will print the information for that sub-package. The versions + for different parts of a split package don't have to be the same! + +## BUGS + +Generating `.pot` files for the prose functions is a pain. The +libretools Makefiles have rules to do it, but it might make sense to +pull it into a separate program. + +`term_title` currently only knows about the terminals screen, tmux, +xterm and rxvt (and their various <TERM> values; +"rxvt-unicode-256color" is still rxvt). + +Also, I think `abort` calling `cleanup 1` would make more sense than +`cleanup 0`. + +## SEE ALSO + +librelib(7), gettext(1), common.sh(3) + +Things that were mentioned: + +bash(1), exit(1), isatty(3), libretools.conf(5), makepkg(8), +printf(1), signal(7) diff --git a/src/lib/messages.sh.3.ronn b/src/lib/messages.sh.3.ronn new file mode 120000 index 0000000..391ecbd --- /dev/null +++ b/src/lib/messages.sh.3.ronn @@ -0,0 +1 @@ +libremessages.1.ronn
\ No newline at end of file diff --git a/src/librefetch/librefetch b/src/librefetch/librefetch index 086a5e9..82c8703 100755 --- a/src/librefetch/librefetch +++ b/src/librefetch/librefetch @@ -1,7 +1,7 @@ #!/usr/bin/env bash # librefetch -# -# Copyright 2013 Luke Shumaker <lukeshu@sbcglobal.net> +# +# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net> # # This file is part of Parabola. # @@ -18,60 +18,58 @@ # You should have received a copy of the GNU General Public License # along with Parabola. If not, see <http://www.gnu.org/licenses/>. -. $(librelib conf.sh) -. libremessages +. $(librelib conf) +. $(librelib messages) +setup_traps declare -r tempdir="$(mktemp -d --tmpdir ${0##*/}.XXXXXXXXXXX)" -cleanup() { rm -rf -- "$tempdir"; } -trap cleanup EXIT +trap "rm -rf -- $(printf '%q' "$tempdir")" EXIT cmd=${0##*/} usage() { - print "Usage: %s [options] <source-url> [<output-file>]" "$cmd" - print "Usage: %s -[g|P|V|h]" "$cmd" + print "Usage: %s [OPTIONS] SOURCE_URL [OUTPUT_FILE]" "$cmd" + print "Usage: %s -[g|P|h]" "$cmd" print "Downloads or creates a liberated source tarball." echo - print "The default mode is to create <output-file>, first by trying download" - print "mode, then create mode." + prose "The default mode is to create OUTPUT_FILE, first by trying + download mode, then create mode." + echo + prose "If OUTPUT_FILE isn't specified, it defaults to the non-directory + part of SOURCE_URL, in the current directory." echo - print "If <output-file> isn't specified, it defaults to the non-directory" - print "part of <source-url>, in the current directory." + prose "Unless '-C' is specified, if SOURCE_URL does not begin with a + configured mirror, create mode is inhibited." echo - print "In download mode, the glob '*://' is stripped from the beginning of" - print "<source-url>, and the resulting path is attempted to be downloaded" - print "from the configured mirror." + prose "In download mode, it simply tries to download SOURCE_URL. At the + beginning of a URL, 'libre://' expands to the first configured + mirror." echo - print "In create mode, it looks at a build script, and uses that to create" - print "the source tarball. <source-url> is ignored, except that it is used" - print "to set the default value of <output-file>." + prose "In create mode, it looks at a build script, and uses that to + create the source tarball. SOURCE_URL is ignored, except that it + is used to set the default value of OUTPUT_FILE." echo - print "The default build script is 'PKGBUILD', or 'SRCBUILD' if it exists." + prose "The default build script is 'PKGBUILD', or 'SRCBUILD' if it + exists." echo - print "Unrecognized options are passed straight to makepkg." + prose "Unrecognized options are passed straight to makepkg." + echo + prose "%s does NOT support getopt-style flag combining. You must use + '-a -b', not '-ab'." "$cmd" echo print "Example usage:" - print ' $ %s libre://mypackage-1.0.tar.gz' "$cmd" + print ' $ %s https://repo.parabolagnulinux.org/other/mypackage/mypackage-1.0.tar.gz' "$cmd" echo print "Options:" print " Settings:" - print " -C Force create mode (don't download)" - print " -D Force download mode (don't create)" - print " -p <file> Use an alternate build script (instead of 'PKGBUILD')" - print " If an SRCBUILD exists in the same directory, it is used" - print " instead" + flag "-C" "Force create mode (don't download)" + flag "-D" "Force download mode (don't create)" + flag "-p <$(_ FILE)>" "Use an alternate build script (instead of + 'PKGBUILD') If an SRCBUILD exists in the same + directory, it is used instead" print " Alternate modes:" - print " -g, --geninteg Generage integrity checks for source files" - print " -P, --print Print the effective build script (SRCBUILD)" - print " -V, --version Show version information" - print " -h, --help Show this message" -} - -version() { - print "librefetch (libretools) beta 4" - echo - print "Copyright (C) 2013 Luke Shumaksr <lukeshu@sbcglobal.net>" - print "This is free software; see the source for copying conditions." - print "There is NO WARRANTY, to the extent permitted by law." + flag "-g, --geninteg" "Generage integrity checks for source files" + flag "-P, --print" "Print the effective build script (SRCBUILD)" + flag "-h, --help" "Show this message" } main() { @@ -81,12 +79,8 @@ main() { mode=download-create parse_options "$@" - # Mode: version, help ################################################## + # Mode: help ########################################################### - if [[ $mode =~ version ]]; then - version - return 0 - fi if [[ $mode =~ help ]]; then usage return 0 @@ -97,20 +91,20 @@ main() { local BUILDFILEDIR="${BUILDFILE%/*}" if [[ -f "${BUILDFILEDIR}/SRCBUILD" ]]; then BUILDFILE="${BUILDFILEDIR}/SRCBUILD" - srcbuild="$(modified_srcbuild "$BUILDFILE")" - else - srcbuild="$(modified_pkgbuild "$BUILDFILE")" fi + if [[ ! -f "$BUILDFILE" ]]; then + error "%s does not exist." "$BUILDFILE" + exit 1 + fi + case "$BUILDFILE" in + */SRCBUILD) srcbuild="$(modified_srcbuild "$BUILDFILE")";; + *) srcbuild="$(modified_pkgbuild "$BUILDFILE")";; + esac makepkg="$(modified_makepkg "$(which makepkg)")" # Mode: checksums ###################################################### if [[ $mode =~ checksums ]]; then - if [[ ${#extra_opts[@]} != 0 ]]; then - print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >> /dev/stderr - usage >> /dev/stderr - return 1 - fi "$makepkg" "${makepkg_opts[@]}" -g -p "$srcbuild" | case ${BUILDFILE##*/} in PKGBUILD) sed -e 's/^[a-z]/mk&/' -e 's/^\s/ &/';; @@ -122,45 +116,52 @@ main() { # Mode: print ########################################################## if [[ $mode =~ print ]]; then - if [[ ${#extra_opts[@]} != 0 ]]; then - print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >> /dev/stderr - usage >> /dev/stderr - return 1 - fi cat "$srcbuild" return 0 fi ######################################################################## - local src dst - case ${#extra_opts[@]} in - 1) - src="${extra_opts[0]#*://}" - dst="$(readlink -m -- "${src##*/}")" - ;; - 2) - src="${extra_opts[0]#*://}" - dst="$(readlink -m -- "${extra_opts[1]}")" - ;; - *) - print "%s: %d non-flag arguments found, expected 1 or 2: %s" "$cmd" ${#extra_opts[@]} >> /dev/stderr - usage >> /dev/stderr - return 1 - esac + local src="${extra_opts[0]}" + local dst="${extra_opts[1]:-${src##*/}}" + + # Don't canonicalize $src unless mode =~ download, and we've validated + # that $MIRRORS is configured. + + # Canonicalize $dst + dst="$(readlink -m -- "$dst")" # Mode: download ####################################################### if [[ $mode =~ download ]]; then load_files librefetch - check_vars librefetch MIRROR DOWNLOADER || return 1 + check_vars librefetch MIRRORS DOWNLOADER || return 1 + + # Canonicalize $src + if [[ "$src" == libre://* ]]; then + src="${MIRRORS[0]}/${src#libre://}" + fi - local url="${MIRROR}/${src}" + # check to see if $src is a candidate for create mode + local inmirror=false; + local mirror + for mirror in "${MIRRORS[@]}"; do + if [[ "$src" == "$mirror"* ]]; then + inmirror=true + break + fi + done + if ! $inmirror; then + # inhibit create + mode=download + fi local dlcmd="${DOWNLOADER}" - dlcmd="${dlcmd//\%o/\"$dst\"}" - dlcmd="${dlcmd//\%u/\"$url\"}" - { eval "$dlcmd"; } >> /dev/stderr && return 0 + [[ $dlcmd = *%u* ]] || dlcmd="$dlcmd %u" + dlcmd="${dlcmd//\%o/"\$dst"}" + dlcmd="${dlcmd//\%u/"\$src"}" + + { eval "$dlcmd"; } >&2 && return 0 fi # Mode: create ######################################################### @@ -172,16 +173,23 @@ main() { export pkg_file=$dst cd "$BUILDFILEDIR" - "$makepkg" "${makepkg_opts[@]}" -p "$srcbuild" >> /dev/stderr || return $? + "$makepkg" "${makepkg_opts[@]}" -p "$srcbuild" >&2 || return $? fi } # sets the variables BUILDFILE, makepkg_opts, extra_opts, mode parse_options() { - # Detect makepkg options that take a second argument - local makepkg_orig="$(which "${MAKEPKG:-makepkg}")" - local makepkg_opt2long=($("${makepkg_orig}" -h | sed -rn 's/\s*(--\S*) <.*/\1/p')) - local makepkg_opt2short=($("${makepkg_orig}" -h | sed -rn 's/\s*(-.) <.*/\1/p')) + # Detect makepkg options + local makepkg_orig="$(which makepkg)" + # --long flags that take a second argument + local makepkg_opt2long=( $(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/\s*(--\S*) <.*/\1/p')) + # -s hort flags that take a second argument + local makepkg_opt2short=($(LC_ALL=C "${makepkg_orig}" -h | sed -rn 's/\s*(-.) <.*/\1/p')) + # all flags + local makepkg_argall=( $(LC_ALL=C "${makepkg_orig}" -h | sed -rn \ + -e 's/^ +(-.) .*/\1/p' \ + -e 's/^ +(-.), (--\S*) .*/\1\n\2/p' \ + -e 's/^ +(--\S*) .*/\1/p')) local opt local have_opt @@ -204,18 +212,42 @@ parse_options() { -g|--geninteg) mode=checksums;; -P|--print) mode=print;; -p) BUILDFILE="$(readlink -m -- "$opt")";; - -V|--version) mode=version;; -h|--help) mode=help;; + --) shift; break;; -*) - makepkg_opts+=("$arg") - $have_opt && makepkg_opts+=("$opt") + if in_array "${arg}" "${makepkg_argall[@]}"; then + makepkg_opts+=("$arg") + $have_opt && makepkg_opts+=("$opt") + else + printf '%s: invalid flag: %s' "$cmd" "$arg" + return 1 + fi ;; - --) shift; break;; *) extra_opts+=("$arg");; esac shift done extra_opts+=("$@") + + # check the number of extra_opts + case "$mode" in + help) # don't worry about it + :;; + checksums|print) # don't take any extra arguments + if [[ ${#extra_opts[@]} != 0 ]]; then + print "%s: found extra non-flag arguments: %s" "$cmd" "${extra_opts[*]}" >&2 + usage >&2 + return 1 + fi + ;; + *download*|*create*) # take 1 or 2 extra arguments + if [[ ${#extra_opts[@]} != 1 ]] && [[ ${#extra_opts[@]} != 2 ]]; then + print "%s: %d non-flag arguments found, expected 1 or 2: %s" "$cmd" ${#extra_opts[@]} >&2 + usage >&2 + return 1 + fi + ;; + esac } # Modify makepkg ############################################################### @@ -232,7 +264,11 @@ makepkg_modify=' /tidy_install\(\) \{/,/^\}$/ { /for .*PURGE_TARGETS/itidy_install_purge /for .*PURGE_TARGETS/,/done/d - /^\}$/ifind . -exec touch --date="1990-01-01 0:0:0 +0" {} + + /^\}$/ifind . -exec touch --no-dereference --date="1990-01-01 0:0:0 +0" -- {} + +} + +/download_sources\(\) \{/ { + arm -rf "$srcdir"\nmkdir "$srcdir" } s|srcdir=.*|&-libre| @@ -298,8 +334,8 @@ checkdepends=() makedepends=("${mkdepends[@]}") #### -options+=(!strip docs libtool emptydirs !zipman purge !upx) -PURGE_TARGETS+=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) +options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx) +PURGE_TARGETS=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) #### if ! declare -f mksource >/dev/null; then diff --git a/src/librefetch/librefetch.8.ronn b/src/librefetch/librefetch.8.ronn index 7fa15d4..7d2dfb3 100644 --- a/src/librefetch/librefetch.8.ronn +++ b/src/librefetch/librefetch.8.ronn @@ -3,32 +3,40 @@ librefetch(8) -- downloads or creates a liberated source tarball ## SYNOPSIS -`librefetch` [options] <source-url> [<output-file>]<br> -`librefetch` -[g|V|h] +`librefetch` [<OPTIONS>] <SOURCE-URL> [<OUTPUT-FILE>]<br> +`librefetch` `-`[`g`|`P`|`h`] ## DESCRIPTION `librefetch` is a program to streamline creation of custom source tarballs for `PKGBUILD(5)` files. -To automatically use `librefetch` to download or create a source -tarball, you can add `libre://FILENAME.tar.gz` to the source array in -your `PKGBUILD`. This works because a post-install script for the -package adds `librefetch` as a download agent for `libre://` to -`makepkg.conf`. Because of this, it is almost never necessary to call -`librefetch` manually. +If a URL mentioned in the `source` array in a `PKGUILD` is in a +location that Parabola uploads "custom" source tarballs (or configured +locations), and no file is at that URL, librefetch will automatically +create it for you. -There are 7 modes: +This works because a post-install script for the package configures +`librefetch` as the download agent for `https://` URLs in +`makepkg.conf`; allowing it to jump in and create a file if need be. +Because of this, it is almost never necessary to call `librefetch` +manually. + +The post-install script also configures `librefetch` as the download +agent for `libre://` URLs, for compatability with PKGBUILDs that used +a previous version of librefetch. + +There are 5 modes: - * `download-create`: The default mode. First try `download` mode, - then `create` mode. * `download`: Download the tarball from the configured mirror. * `create`: Create the tarball from a `PKGBUILD`/`SRCBUILD`. * `checksums`: Generate integrity checks for source files. * `print`: Print the effective build script. - * `version`: Print `librefetch` version information. * `help`: Print `librefetch` usage information. +The normal mode of operation is `download` mode. If `download` mode +fails, it may choose to try `create` mode. + ## OPTIONS * `-C`: Force `create` mode (don't download) @@ -39,9 +47,26 @@ There are 7 modes: * `-g` | `--geninteg`: Use `checksums` mode: Generate integrity checks for source files. * `-P` | `--print`: Use `print` mode: print the effective build script. - * `-V` | `--version`: Use `version` mode: Show version information. * `-h` | `--help`: Use `help` mode: Show useage information. +## DOWNLOAD MODE + +If <SOURCE-URL> begins with the string `libre://`, it is replaced with +the first value in `MIRRORS`, as configured in `librefetch.conf(5)`; +this is for compatability with PKGBUILDs that used a previous version +of librefetch. + +It uses `DOWNLOADER`, as configured in `librefetch.conf` to attempt to +download the source tarball from that URL. If that fails, and +following conditions are met, it proceeds to `create` mode: + + * The `-D` flag has not been specified to inhibit `create` mode. + * The `<source-url>` begins with one of the values in `MIRRORS`. + +The latter requirement allows librefetch to be used as a generic +HTTP(S) download agent, that can automatically create files from +whitelisted locations. + ## CREATE MODE The principle of `create` mode is that a special `PKGBUILD(5)`, called @@ -128,11 +153,11 @@ Other changes: * `options=()` is set have `makepkg` avoid making changes to `$pkgdir`. The exact change is: - options+=(!strip docs libtool emptydirs !zipman purge !upx) + options=(!strip docs libtool staticlibs emptydirs !zipman purge !upx) * `PURGE_TARGETS=()` has vcs directories added to it: - PURGE_TARGETS+=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) + PURGE_TARGETS=(.bzr/ .cvs/ .git/ .hg/ .svn/ .makepkg/) ### MAKEPKG MODIFICATIONS diff --git a/src/librefetch/librefetch.conf b/src/librefetch/librefetch.conf index 40d2078..6948e8d 100644 --- a/src/librefetch/librefetch.conf +++ b/src/librefetch/librefetch.conf @@ -1,2 +1,5 @@ -MIRROR='https://repo.parabolagnulinux.org/sources/' +MIRRORS=( + 'https://repo.parabolagnulinux.org/sources/' + 'https://repo.parabolagnulinux.org/other/' +) DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u' diff --git a/src/librefetch/librefetch.conf.5.ronn b/src/librefetch/librefetch.conf.5.ronn index 3d80ab5..6158104 100644 --- a/src/librefetch/librefetch.conf.5.ronn +++ b/src/librefetch/librefetch.conf.5.ronn @@ -24,13 +24,19 @@ If `$XDG_CONFIG_HOME` is not set, a default value is set: ## OPTIONS - * `MIRROR='https://repo.parabolagnulinux.org/sources/'`: - The location to download pre-built source tarball in download - mode. + * `MIRRORS=( ... )`: + A list of locations that generated source tarballs may be located + at. If a URL begins with `libre://`, then `libre://` is replaced + with the first location listed here. * `DOWNLOADER='/usr/bin/curl -fLC - --retry 3 --retry-delay 3 -o %o %u'`: The HTTP client to use when downloading pre-built source tarballs - in download mode. + in download mode. The format and semantics are similar to + `DLAGENTS` in `makepkg.conf`(5). If present, `%u` will be replaced + with the download URL (correctly quoted), otherwise the download + URL will be appended to the end of the command. If present, `%o` + will be replaced with the local filename that it should be + downloaded to. ## SEE ALSO -librefetch(8) +librefetch(8), makepkg.conf(5) diff --git a/src/libregit/libregit b/src/libregit/libregit deleted file mode 100755 index 8687d2f..0000000 --- a/src/libregit/libregit +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2012-2013 Pacman Development Team <pacman-dev@archlinux.org> -# Copyright (c) 2012-2013 Luke Shumaker <lukeshu@sbcglobal.net> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -. libremessages - -cd_safe() { - if ! cd "$1"; then - error "Failed to change to directory %s" "$1" - plain "Aborting..." - exit 1 - fi -} - -download_git() { - if [[ ! -d "$dir/.git" ]] ; then - msg2 "Cloning %s %s repo..." "${repo}" "git" - if ! git clone "$repo" "$dir"; then - error "Failure while downloading %s %s repo" "${repo}" "git" - plain "Aborting..." - exit 1 - fi - else - cd_safe "$dir" - # Make sure we are fetching the right repo - if [[ "$repo" != "$(git config --get remote.origin.url)" ]] ; then - error "%s is not a clone of %s" "$dir" "$repo" - plain "Aborting..." - exit 1 - fi - msg2 "Updating %s %s repo..." "${repo}" "git" - if ! git pull origin "$ref"; then - # only warn on failure to allow offline builds - warning "Failure while updating %s %s repo" "${repo}" "git" - fi - fi -} - -usage() { - print 'Usage: %s repo ref dir' "${0##*/}" - echo - print "Clones or pulls from the git URL 'repo', and checks out the git ref" - print "'ref' to the directory 'dir'." - echo - print "It does safety checks, figures out whether to clone or pull, and other" - print "helpful things. This exists because the same 'download_git' function" - print "from makepkg was being copied again and again." -} - -main() { - [[ $# == 3 ]] || { usage >&2; return 1; } - repo=$1 - ref=$2 - dir=$3 - - [[ -d "${dir%/*}" ]] || mkdir -p "${dir%/*}" - download_git -} - -main "$@" diff --git a/src/libretools.conf b/src/libretools.conf index 593aed6..4b1973a 100644 --- a/src/libretools.conf +++ b/src/libretools.conf @@ -5,14 +5,14 @@ ################################################################################ # The dir where you work on -WORKDIR=/home/$LIBREUSER/packages +WORKDIR="$LIBREHOME/packages" ## Blacklist URL BLACKLIST=https://repo.parabolagnulinux.org/docs/blacklist.txt ## Diff tool (vimdiff, gvimdiff, meld, etc) ## Used by `aur`, `diff-unfree` -DIFFTOOL=`which kdiff3 meld gvimdiff vimdiff colordiff diff 2>/dev/null|sed 's/\s.*//;1q'` +DIFFPROG=`which kdiff3 meld gvimdiff vimdiff colordiff diff 2>/dev/null|sed 's/\s.*//;1q'` ## The repos you'll be packaging for ## Used by `toru`, `createworkdir` @@ -27,17 +27,17 @@ REPOS=('core' 'libre' 'extra' 'community' 'libre-testing' 'social' 'sugar' 'pcr' ARCHES=('x86_64' 'i686' 'mips64el') ## ABSLibre -#ABSLIBREGIT=http://projects.parabolagnulinux.org/abslibre.git -ABSLIBREGIT=ssh://git@projects.parabolagnulinux.org:1863/srv/git/abslibre.git +## Used by `createworkdir` +ABSLIBRERECV=git://projects.parabolagnulinux.org/abslibre.git +ABSLIBRESEND=ssh://git@projects.parabolagnulinux.org:1863/srv/git/abslibre.git ################################################################################ # librerelease # ################################################################################ ## Where to upload packages to -# Don't change unless you know what you're doing and you won't screw -# anything ;) -REPODEST=repo@repo:/srv/http/repo/public +# '/staging/' is appended; this is for compatability with previous versions. +REPODEST=repo@repo:/srv/http/repo/staging/$LIBREUSER ## Assumes something similar in your .ssh/config: # Host repo # Port 1863 diff --git a/src/mips64el-tools/mipsrelease b/src/mips64el-tools/mipsrelease index de17651..ea15f85 100755 --- a/src/mips64el-tools/mipsrelease +++ b/src/mips64el-tools/mipsrelease @@ -5,7 +5,7 @@ # Called by HOOKLOCALRELEASE # $1 repo -# $2+ packages +# $2+ packages . libremessages . $(librelib conf.sh) diff --git a/src/pkgbuild-check-licenses b/src/pkgbuild-check-licenses deleted file mode 100755 index 85ca2c3..0000000 --- a/src/pkgbuild-check-licenses +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env bash -# pkgbuild-check-licenses - -# Copyright 2010 Haase Hernández -# Copyright 2010 Joseph Graham -# Copyright 2010 Joshua Ismael -# Copyright 2010 Nicolás Reynolds -# Copyright 2012-2013 Luke Shumaker -# -# This file is part of Parabola. -# -# Parabola is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Parabola is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Parabola. If not, see <http://www.gnu.org/licenses/>. - -. libremessages -. $(librelib conf) - -# Usage: check_deps $pkgbuild -# Check whether a PKGBUILD package depends on non-free packages -check_licenses() ( - # Note that we use () instead of {} for this function; so that variables - # from the PKBUILD don't bubble up - local pkgbuild=$1 - load_PKGBUILD "$pkgbuild" - if [[ -z $pkgname ]]; then - return $_E_ERROR # not a PKGBUILD - fi - if [[ -z "${license[*]}" ]]; then - error "license array of %s %s is not set" "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" - return $_E_ERROR - fi - - msg2 "Looking at license array of %s %s" "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" - - local ret=$_E_OK - for _license in "${license[@]}"; do - if [[ ! -e "/usr/share/licenses/common/$_license" ]]; then - local s=$_E_OK - case "${_license#custom:}" in - WTFPL) - # accept as common, I think it should be in the licenses package - :;; - BSD1|BSD2|BSD3|MIT|X11) - # accept these as common; they can't be included in the - # 'licenses' package because some text must be customized - :;; - BSD4) - warning "The 4-clause BSD license is free but has practical problems.";; - BSD) - warning "License 'BSD' is ambiguous, use 'BSD{1..4}' to specify the number of clauses." - s=$_E_UNCOMMON - ;; - JSON) - error "License '%s' is a known non-free license." "$_license" - s=$_E_NONFREE - ;; - *) - warning "License '%s' is not a common license." "$_license" - s=$_E_UNCOMMON - ;; - esac - ret=$(($ret|$s)) - fi - done - return $ret -) - -usage() { - print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}" - echo - prose 'If no PKGBUILD is specified, `./PKGBUILD` is implied.' - echo - print "Exit status (add them for combinations):" - print " 0: Everything OK, no freedom issues" - print " 1: Ran with error" - print " 2: Uses uncommon licenses, check them" - print " 4: Uses known unacceptable licenses" - echo - print "Options:" - flag '-f' 'Allow running as root user' - flag '-h' 'Show this message' -} -_E_OK=0 -_E_ERROR=1 -_E_UNCOMMON=2 -_E_NONFREE=4 - -main() { - local asroot=false - while getopts 'fh' arg; do - case "$arg" in - f) asroot=true;; - h) usage; return $_E_OK;; - *) usage; return $_E_ERROR;; - esac - done - shift $(($OPTIND - 1)) - if [[ $# -lt 1 ]]; then - pkgbuilds=("`pwd`/PKGBUILD") - else - pkgbuilds=("$@") - fi - - if [[ -w / ]] && ! $asroot; then - error "Run as normal user, or use the -f option to run as root." - return 1 - fi - - local ret=0 - for pkgbuild in "${pkgbuilds[@]}"; do - check_licenses "$pkgbuild" || ret=$(($ret|$?)) - done - return $ret -} - -main "$@" diff --git a/src/pkgbuild-check-nonfree b/src/pkgbuild-check-nonfree index 1cc0d9b..7b73745 100755 --- a/src/pkgbuild-check-nonfree +++ b/src/pkgbuild-check-nonfree @@ -1,10 +1,12 @@ #!/usr/bin/env bash +# -*- tab-width: 4 ; sh-basic-offset: 4 -*- # pkgbuild-check-nonfree -# Copyright 2010 Haase Hernández -# Copyright 2010 Joseph Graham -# Copyright 2010 Joshua Ismael -# Copyright 2012-2013 Luke Shumaker +# Copyright (C) 2010 Haase Hernández +# Copyright (C) 2010 Joseph Graham +# Copyright (C) 2010 Joshua Ismael +# Copyright (C) 2010 Nicolás Reynolds +# Copyright (C) 2012-2013 Luke Shumaker # # This file is part of Parabola. # @@ -21,80 +23,61 @@ # You should have received a copy of the GNU General Public License # along with Parabola. If not, see <http://www.gnu.org/licenses/>. -. libremessages -. libreblacklist -. $(librelib conf) - -# Usage: check_deps $pkgbuild -# Check whether a PKGBUILD package depends on non-free packages -check_deps() ( - # Note that we use () instead of {} for this function; so that variables - # from the PKBUILD don't bubble up - local pkgbuild=$1 - load_PKGBUILD "$pkgbuild" - if [[ -z "$pkgname" ]]; then - exit 1 # not a PKGBUILD - fi +# I appologize that this program got *huge*. +# It's not complicated, just long. - msg2 'Looking for unfree dependencies of %s %s' "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" - - local pkgs=( - # packages being built - "${pkgname[@]}" - # depends - "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" "${optdepends[@]%%:*}" - # mksource depends - "${mkdepends[@]}" - ) - local ret=0 - for pkg in "${pkgs[@]}"; do - local line="$(blacklist-cat|blacklist-lookup "$pkg")" - local rep="$(blacklist-get-rep <<<"$line")" - if [[ -z $line ]]; then - # not mentioned in blacklist; free - plain '%s: not blacklisted' "$pkg" - continue - elif [[ -z $rep ]]; then - # non-free with no replacement - plain '%s: blacklisted' "$pkg" - ret=1 - else - # non-free with free replacement - if [[ "$rep" == "$pkg" ]]; then - plain '%s: repackaged with the same name' "$pkg" - else - plain '%s: replaced by %s' "$pkg" "$rep" - fi - fi - done - return $ret -) +. $(librelib messages) +. $(librelib conf) +. $(librelib blacklist) usage() { print "Usage: %s [OPTIONS] [PKGBUILD1 PKGBUILD2 ...]" "${0##*/}" + print "Analyzes a PKGBUILD for freedom issues" echo prose 'If no PKGBUILD is specified, `./PKGBUILD` is implied.' echo - print "Exit status:" + print "Exit status (add them for combinations):" print " 0: Everything OK, no freedom issues" print " 1: Ran with error" - print " 15: Depends on non-free packages" + print "Warning-level freedom issues:" + print " 2: Uses unrecognized licenses, check them" + print " 4: Uses GPL-incompatible licenses" + print "Error-level freedom issues:" + print " 8: Uses known unacceptable licenses" + print " 16: Has nonfree dependencies" + print " 32: Is a known nonfree package" echo print "Options:" - flag '-c' 'Use the cached blacklist, do not try downloading.' + flag '-c' 'Use the cached blacklist, do not try downloading' flag '-f' 'Allow running as root user' + echo + flag '-q' 'Be quiet' + flag '-v' 'Be verbose' + echo flag '-h' 'Show this message' } +# Make sure these match pkgbuild-summarize-nonfree +declare -ri _E_OK=0 +declare -ri _E_ERROR=1 +declare -ri _E_LIC_UNKNOWN=2 +declare -ri _E_LIC_NOGPL=4 +declare -ri _E_LIC_NONFREE=8 +declare -ri _E_DEP_NONFREE=16 +declare -ri _E_PKG_NONFREE=32 main() { - local asroot=false + # Parse flags local cache=false - while getopts 'cfh' arg; do + local asroot=false + local v=1 + while getopts 'cfqvh' arg; do case "$arg" in c) cache=true;; f) asroot=true;; - h) usage; return 0;; - *) usage; return 1;; + q) v=0;; + v) v=2;; + h) usage; return $_E_OK;; + *) usage >&2; return $_E_ERROR;; esac done shift $(($OPTIND - 1)) @@ -104,18 +87,222 @@ main() { pkgbuilds=("$@") fi + # Do a check to see if we are running as root if [[ -w / ]] && ! $asroot; then error "Run as normal user, or use the -f option to run as root." return 1 fi - $cache || blacklist-update || return 1 + # Adjust the verbosity + if [[ $v == 0 ]]; then + error() { :; } + warning() { :; } + plain() { :; } + info() { :; } + elif [[ $v == 1 ]]; then + info() { :; } + elif [[ $v == 2 ]]; then + info() { plain "$@"; } + fi + + # Update the blacklist + $cache || blacklist-update || return $_E_ERROR - local ret=0 + # Do the work + declare -i ret=0 + local pkgbuild for pkgbuild in "${pkgbuilds[@]}"; do - check_deps "$pkgbuild" || ret=15 + pkgbuild_check "$pkgbuild" || ret=$(($ret|$?)) done return $ret } +# Helper functions ############################################################# +# These should maybe be moved into lib/conf.sh + +# Usage: var="$(pkgbuild_get_pkg_str ${pkgname} ${varname})" +# Gets a package-level string for a split-package +pkgbuild_get_pkg_str() { + [[ $# == 2 ]] || panic 'malformed call to pkgbuild_get_pkg_str' + local pkg=$1 + local var=$2 + + local indirect=${!var} + eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p") + printf '%s' "${indirect}" +} +# Usage: eval $(pkgbuild_get_pkg_ary ${pkgname} ${varname} [$variable_name_to_set]) +# Gets a package-level array for a split-package +pkgbuild_get_pkg_ary() { + [[ $# == 2 ]] || [[ $# == 3 ]] || panic 'malformed call to pkgbuild_get_pkg_ary' + local pkg=$1 + local var=$2 + local out="${3:-$var}" + + local ary="${var}[@]" + local indirect=("${!ary}") + eval $(declare -f package_$pkg | sed -rn "s/^\s*${var}(\+?=)/indirect\1/p") + declare -p indirect|sed "s/ indirect=/ ${out}=/" +} + +# Checker functions ############################################################ + +# Usage: check_lic "${licence}" +# Check a license name to see if it is OK +check_lic() { + [[ $# == 1 ]] || panic 'malformed call to check_license' + local license=$1 + + info 'Checking license: %s' "$license" + + if [[ -e "/usr/share/licenses/common/$license" ]]; then + return $_E_OK + else + case "${license#custom:}" in + WTFPL) + # accept as common, I think it should be in the licenses package + return $_E_OK;; + BSD1|BSD2|BSD3|MIT|X11) + # accept these as common; they can't be included in the + # 'licenses' package because some text must be customized + return $_E_OK;; + BSD4) + warning "The 4-clause BSD license is free but has practical problems." + return $_E_LIC_NOGPL;; + BSD) + warning "License 'BSD' is ambiguous, use 'BSD{1..4}' to specify the number of clauses." + return $_E_LIC_UNKNOWN;; + JSON) + error "License '%s' is a known non-free license." "$license" + return $_E_LIC_NONFREE;; + *) + warning "License '%s' is not a common (recognized) license." "$license" + return $_E_LIC_UNKNOWN;; + esac + fi + panic 'code should never be reached' +} + +# Usage: check_dep "${dependency}" +# Checks for ${dependency} in the blacklist +check_dep() { + [[ $# == 1 ]] || panic 'malformed call to check_dep' + local pkg=$1 + + local line="$(blacklist-cat|blacklist-lookup "$pkg")" + local rep="$(blacklist-get-rep <<<"$line")" + if [[ -z $line ]]; then + # not mentioned in blacklist; free + info '%s: not blacklisted' "$pkg" + return $_E_OK + elif [[ -z $rep ]]; then + # non-free with no replacement + plain '%s: blacklisted' "$pkg" + return $_E_DEP_NONFREE + else + # non-free with free replacement + if [[ "$rep" == "$pkg" ]]; then + info '%s: repackaged with the same name' "$pkg" + else + info '%s: replaced by %s' "$pkg" "$rep" + fi + return $_E_OK + fi + panic 'code should never be reached' +} + +# Usage: check_pkg "${pkgname}" +# Checks for ${pkgname} in the blacklist +check_pkg() { + [[ $# == 1 ]] || panic 'malformed call to check_pkg' + check_dep "$@" + case $? in + $_E_OK) + return $_E_OK;; + $_E_DEP_NONFREE) + return $_E_PKG_NONFREE;; + *) + panic 'unexpected return code from check_dep';; + esac + panic 'code should never be reached' +} + +# Usage: pkgbuild_ckec $pkgbuild +# Check whether a PKGBUILD has any issues (using the above) +pkgbuild_check() ( + [[ $# == 1 ]] || panic 'malformed call to pkgbuild_check' + local pkgbuild=$1 + + load_PKGBUILD "$pkgbuild" + if [[ -z $pkgname ]]; then + return $_E_ERROR # not a PKGBUILD + fi + + declare -i ret=0 # the return status + local dep lic # iterators for us in `for` loops + local ck_deps ck_lics # lists of deps and licenses that have been checked + + if [[ ${#pkgname[@]} == 1 ]]; then + msg2 'Inspecting package pkgname=%q (%s)' "$pkgname" "$(get_full_version)" + else + msg2 'Inspecting split package pkgbase=%q (%s)' "${pkgbase:-${pkgname[0]}}" "$(get_full_version)" + fi + + # Check if this is blacklisted + check_pkg "${pkgbase:-${pkgname[0]}}" || ret=$(($ret|$?)) + # Check if dependencies are blacklisted + for dep in "${depends[@]}" "${makedepends[@]}" "${checkdepends[@]}" \ + "${optdepends[@]%%:*}" "${mkdepends[@]}" + do + check_dep "$dep" || ret=$(($ret|$?)) + ck_deps+=("$dep") + done + # Check the licenses + for lic in "${license[@]}"; do + check_lic "$lic" || ret=$(($ret|$?)) + ck_lics+=("$lic") + done + + if [[ ${#pkgname[@]} == 1 ]]; then + # Non-split package + # Make sure a license is set + if [[ ${#ck_lics[@]} == 0 ]]; then + error "The license array is empty" + ret=$(($ret|$_E_ERROR)) + fi + else + # Split package + # Check the individual split packages + local _pkgname _license _depends _optdepends + for _pkgname in "${pkgname[@]}"; do + msg2 'Inspecting split package pkgname=%q (%s)' "$_pkgname" "$(get_full_version "$_pkgname")" + eval $(pkgbuild_get_pkg_ary "$_pkgname" license _license) + eval $(pkgbuild_get_pkg_ary "$_pkgname" depends _depends) + eval $(pkgbuild_get_pkg_ary "$_pkgname" optdepends _optdepends) + + # Check if this is blacklisted + check_pkg "$_pkgname" || ret=$(($ret|$?)) + # Check if dependencies are blacklisted + for dep in "${_depends[@]}" "${_optdepends[@]%%:*}"; do + if ! in_array "$dep" "${ck_deps[@]}"; then + check_dep "$dep" || ret=$(($ret|$?)) + fi + done + # Check the licenses + for lic in "${_license[@]}"; do + if ! in_array "$lic" "${ck_lics[@]}"; then + check_lic "$lic" || ret=$(($ret|$?)) + fi + done + + if [[ ${#_license[@]} == 0 ]]; then + error "The license array is empty" + ret=$(($ret|$_E_ERROR)) + fi + done + fi + + return $ret +) + main "$@" diff --git a/src/pkgbuild-summarize-nonfree b/src/pkgbuild-summarize-nonfree new file mode 100755 index 0000000..7b005d5 --- /dev/null +++ b/src/pkgbuild-summarize-nonfree @@ -0,0 +1,88 @@ +#!/bin/bash + +. $(librelib messages) + +# Make sure these match pkgbuild-check-nonfree +declare -ri _E_OK=0 +declare -ri _E_ERROR=1 +declare -ri _E_LIC_UNKNOWN=2 +declare -ri _E_LIC_NOGPL=4 +declare -ri _E_LIC_NONFREE=8 +declare -ri _E_DEP_NONFREE=16 +declare -ri _E_PKG_NONFREE=32 + +usage() { + print "Usage: %s [OPTIONS] STATUS" "${0##*/}" + print "Summarizes a status code from pkgbuild-check-nonfree" + echo + prose 'It thresholds the issues it finds, only failing for error-level + issues, and ignoring warnings. Unless `-q` is specified, it also + prints a summary of the issues it found.' + echo + print 'Options:' + flag '-q' 'Be quiet' + flag '-h' 'Show this message' +} + +main() { + local quiet=false + while getopts 'qh' arg; do + case "$arg" in + q) quiet=true;; + h) usage; return 0;; + *) usage >&2; return 1;; + esac + done + shift $(($OPTIND - 1)) + if [[ $# -ne 1 ]]; then + usage >&2 + return 1 + fi + if ! [[ $1 =~ ^[0-9]+$ ]]; then + error 'STATUS must be an integer' + usage >&2 + return 1 + fi + + if $quiet; then + error() { :; } + warning() { :; } + fi + + parse $1; + return $? +} + +parse() { + [[ $# == 1 ]] || panic 'malformed call to parse' + declare -i s=$1; + + declare -i ret=0 + declare -i i + for i in 1 2 4 8 16 32; do + if [[ $(($s & $i)) -gt 0 ]]; then + case $i in + $_E_ERROR) + # could be anything, assume the worst + error "There was an error processing the PKGBUILD" + ret=1;; + $_E_LIC_UNKNOWN) + warning "This PKGBUILD has an unknown license";; + $_E_LIC_NOGPL) + warning "This PKGBUILD has a GPL-incompatible license";; + $_E_LIC_NONFREE) + error "This PKGBUILD has a known nonfree license" + ret=1;; + $_E_DEP_NONFREE) + error "This PKGBUILD depends on known nonfree packages" + ret=1;; + $_E_PKG_NONFREE) + error "This PKGBUILD is for a known nonfree package" + ret=1;; + esac + fi + done + return $ret +} + +main "$@" diff --git a/src/treepkg b/src/treepkg index a1b4e38..73ef93b 100755 --- a/src/treepkg +++ b/src/treepkg @@ -1,6 +1,6 @@ #!/usr/bin/env bash #set -x -# (c) 2012 Nicolás Reynolds <fauno@parabola.nu> +# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -19,15 +19,15 @@ source libremessages source $(librelib conf.sh) load_files libretools -check_vars libretools HOOKPREBUILD FULLBUILDCMD HOOKLOCALRELEASE +check_vars libretools HOOKPREBUILD FULLBUILDCMD HOOKLOCALRELEASE || exit 1 load_files makepkg -term_title "$(basename $0)" +term_title "${0##*/}" # End inmediately but print an useful message trap_exit() { - term_title "error!" - error "($(basename $0)) $@ (leftovers on ${BUILDDIR})" + term_title "${0##*/}: $(_ 'Error!')" + error "%s: %s (leftovers on %s" "${0##*/}" "$*" "${BUILDDIR}" exit 1 } @@ -59,10 +59,10 @@ bury() { # If there's a depth or the package is not the root of the build tree (which # can lead to funny chicken-and-egg problems), update the depth to the current # package next-depth and rename the dir too - if [ -z "${current_depth}" ]; then return; fi - if [ -z "${current_name}" ]; then return; fi - if [ ${current_depth} -eq 0 ]; then return; fi - if [ ${current_depth} -ge $2 ]; then return; fi + if [[ -z "${current_depth}" ]]; then return; fi + if [[ -z "${current_name}" ]]; then return; fi + if [[ ${current_depth} -eq 0 ]]; then return; fi + if [[ ${current_depth} -ge $2 ]]; then return; fi ${VERBOSE} && msg "Burying ${1} from ${current_depth} to ${2}" @@ -78,8 +78,8 @@ guess_repo() { basename "$(dirname "${1}")" } -if [ ! -f PKGBUILD ]; then - error "Missing PKGBUILD ($PWD)" +if [[ ! -f PKGBUILD ]]; then + error "Missing PKGBUILD (%s)" "$PWD" exit 1 fi @@ -97,13 +97,13 @@ unset build package >/dev/null 2>&1 for _pkg in "${pkgname[@]}"; do unset package_${_pkg} >/dev/null 2>&1 || true done -## +## # Get useful values pkgbase="${pkgbase:-${pkgname[0]}}" # Get or set the work dir -BUILDDIR="${1:-$(mktemp -d /tmp/${pkgbase}-treepkg-XXXx)}" +BUILDDIR="${1:-$(mktemp --tmpdir -d ${pkgbase}-treepkg.XXXXXXXXXX)}" BUILDORDER="${BUILDDIR}/BUILDORDER" DEPTH=${2:-0} NEXTDEPTH=$((${DEPTH} + 1)) @@ -182,21 +182,21 @@ if ! ${BUILDNOW}; then fi # Only build at the end -if [ ${DEPTH} -eq 0 ]; then +if [[ ${DEPTH} -eq 0 ]]; then ${VERBOSE} && msg "Starting build" || true if ${BUILD}; then - ${VERBOSE} && msg "Build tree stored in ${BUILDORDER}" || true + ${VERBOSE} && msg "Build tree stored in %s" "${BUILDORDER}" || true # Build everything sorting the build dir # The reverse order ensures we start by the deepest packages for _pkg in $(ls -r "${BUILDDIR}"); do # Ignore if there's no PKGBUILD - if [ ! -f "${BUILDDIR}/${_pkg}/PKGBUILD" ]; then continue; fi + if [[ ! -f "${BUILDDIR}/${_pkg}/PKGBUILD" ]]; then continue; fi # Skip if already built (faster than calling is_build again) - if [ -f "${BUILDDIR}/${_pkg}/built_ok" ]; then continue; fi + if [[ -f "${BUILDDIR}/${_pkg}/built_ok" ]]; then continue; fi - ${VERBOSE} && msg "Building ${_pkg/_/ }" || true + ${VERBOSE} && msg "Building %s" "${_pkg/_/ }" || true # Remove leading zeros and space if any term_title "$(echo ${_pkg/_/ } | sed "s/^0\+ \?//")" @@ -221,11 +221,11 @@ if [ ${DEPTH} -eq 0 ]; then fi if (( CLEANUP )) ; then - msg2 "Removing ${BUILDDIR}" + msg2 "Removing %s" "${BUILDDIR}" rm -rf "${BUILDDIR}" fi fi -term_title "done" +term_title "$(_ Done)" exit $? diff --git a/test/aur-test.sh b/test/aur-test.sh index 1fbe659..5de590b 100644 --- a/test/aur-test.sh +++ b/test/aur-test.sh @@ -13,18 +13,18 @@ after() { } it_displays_help() { - LANG=C aur -h >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C aur -h >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr } it_fails_with_0_args() { aur >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } # TODO: Actually test diff --git a/test/is_built-test.sh b/test/is_built-test.sh index 62847e5..f7f6c65 100644 --- a/test/is_built-test.sh +++ b/test/is_built-test.sh @@ -13,48 +13,48 @@ after() { } it_displays_help() { - LANG=C is_built -h >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C is_built -h >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr } it_fails_with_0_args() { is_built >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat -gt 1 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } it_succeeds_with_1_arg() { is_built sh >$tmpdir/stdout 2>$tmpdir/stderr - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + empty $tmpdir/stderr } it_returns_1_for_non_existent_package() { is_built phony-ne-package 100 >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? - [[ $stat == 1 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + [[ $stat == 1 ]] + empty $tmpdir/stdout + empty $tmpdir/stderr } it_returns_1_for_future_packages() { # If emacs ever goes rapid release, we might need to change this :P is_built emacs 100 >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? - [[ $stat == 1 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + [[ $stat == 1 ]] + empty $tmpdir/stdout + empty $tmpdir/stderr } it_returns_0_for_past_packages() { # If emacs ever goes rapid release, we might need to change this :P is_built emacs 1 >$tmpdir/stdout 2>$tmpdir/stderr - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + empty $tmpdir/stderr } diff --git a/test/lib-blacklist-test.sh b/test/lib-blacklist-test.sh index c496167..9dfd5da 100644 --- a/test/lib-blacklist-test.sh +++ b/test/lib-blacklist-test.sh @@ -45,26 +45,26 @@ it_works_with_colons_in_reason() { v="$(libreblacklist get-reason <<<"$line")"; [[ $v == 'my:reason' ]] } -it_fails_update_when_there_is_no_blacklist_or_network() { +it_fails_update_with_no_blacklist_or_network() { mkdir -p $XDG_CONFIG_HOME/libretools echo "BLACKLIST='phony://example.com'" >$XDG_CONFIG_HOME/libretools/libretools.conf libreblacklist update >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } -it_fails_cat_when_there_is_no_blacklist_or_network() { +it_fails_cat_with_no_blacklist_or_network() { mkdir -p $XDG_CONFIG_HOME/libretools echo "BLACKLIST='phony://example.com'" >$XDG_CONFIG_HOME/libretools/libretools.conf libreblacklist cat >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } it_fails_update_when_BLACKLIST_is_unset() { @@ -74,8 +74,8 @@ it_fails_update_when_BLACKLIST_is_unset() { libreblacklist update >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } it_fails_cat_when_syntax_error_in_conf() { @@ -86,8 +86,8 @@ it_fails_cat_when_syntax_error_in_conf() { libreblacklist cat >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } it_downloads_the_blacklist_as_needed() { @@ -97,7 +97,7 @@ it_downloads_the_blacklist_as_needed() { libreblacklist cat >$tmpdir/stdout 2>$tmpdir/stderr - [[ -n "$(cat $tmpdir/stdout)" ]] + not empty $tmpdir/stdout } it_downloads_the_blacklist_repeatedly() { @@ -110,23 +110,23 @@ it_downloads_the_blacklist_repeatedly() { } it_displays_help_and_fails_with_no_args() { - LANG=C libreblacklist >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + LC_ALL=C libreblacklist >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] + empty $tmpdir/stdout [[ "$(sed 1q $tmpdir/stderr)" =~ 'Usage: libreblacklist ' ]] } it_displays_help_when_given_h() { - LANG=C libreblacklist -h >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C libreblacklist -h >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q $tmpdir/stdout)" =~ 'Usage: libreblacklist ' ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr } it_displays_help_when_given_h_cat() { - LANG=C libreblacklist -h cat >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C libreblacklist -h cat >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q $tmpdir/stdout)" == 'Usage: libreblacklist cat' ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr } diff --git a/test/lib-conf-test.sh b/test/lib-conf-test.sh index 0d028b8..efad907 100644 --- a/test/lib-conf-test.sh +++ b/test/lib-conf-test.sh @@ -16,7 +16,7 @@ it_sets_makepkg_vars_in_custom_file() { unset PKGDEST touch "$tmpdir/makepkg.conf" . $(librelib conf.sh) - MAKEPKG_CONF="$tmpdir/makepkg.conf" set_conf_makepkg PKGDEST /pkgdest + MAKEPKG_CONF="$tmpdir/makepkg.conf" set_var makepkg PKGDEST /pkgdest . "$tmpdir/makepkg.conf" [[ $PKGDEST == /pkgdest ]] } diff --git a/test/lib-messages-test.sh b/test/lib-messages-test.sh index 826556b..3fcac63 100644 --- a/test/lib-messages-test.sh +++ b/test/lib-messages-test.sh @@ -30,15 +30,15 @@ it_works_with_no_color_and_set_euE() ( msg Foo ) >$tmpdir/stdout 2>$tmpdir/stderr - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr ) it_can_be_called_without_including() { libremessages msg Foo >$tmpdir/stdout 2>$tmpdir/stderr - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } it_fails_with_msg_and_no_args() { diff --git a/test/librechroot-test.sh b/test/librechroot-test.sh index 76afb56..c22ac58 100644 --- a/test/librechroot-test.sh +++ b/test/librechroot-test.sh @@ -4,55 +4,96 @@ describe librechroot . ./test-common.sh +_setup_chrootdir + before() { _before librechroot + mkdir -p "$XDG_CONFIG_HOME"/libretools - echo "CHROOTDIR='$tmpdir/chrootdir'" > "$XDG_CONFIG_HOME"/libretools/chroot.conf + + echo "CHROOTDIR='${chrootdir}'" > "$XDG_CONFIG_HOME"/libretools/chroot.conf echo "CHROOT='default'" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf echo "CHROOTEXTRAPKG=()" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf } -after() { +after() ( _after_sudo -} +) it_creates_repo_for_new_chroots() { require network sudo || return 0 libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty - testsudo librechroot run test -r /repo/repo.db + testsudo librechroot -l "$roundup_test_name" run test -r /repo/repo.db } it_cleans_the_local_repo_correctly() { require network sudo || return 0 libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty - testsudo librechroot make - testsudo librechroot clean-repo - testsudo librechroot run test -r /repo/repo.db + testsudo librechroot -l "$roundup_test_name" make + testsudo librechroot -l "$roundup_test_name" clean-repo + testsudo librechroot -l "$roundup_test_name" run test -r /repo/repo.db # TODO: inspect /repo/* more } +it_respects_exit_status_if_out_isnt_a_tty() ( + require network sudo || return 0 + set -o pipefail + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + r=0 + { testsudo librechroot -l "$roundup_test_name" run exit 3 | cat; } || r=$? + [[ $r == 3 ]] +) + +it_creates_ca_certificates() { + require network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo librechroot -l "$roundup_test_name" run test -r /etc/ssl/certs/ca-certificates.crt +} + +it_disables_networking_when_requested() { + require network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + + testsudo librechroot -l "$roundup_test_name" run curl https://repo.parabolagnulinux.org/ >/dev/null + not testsudo librechroot -l "$roundup_test_name" -N run curl https://repo.parabolagnulinux.org/ >/dev/null +} + +it_handles_CHROOTEXTRAPKG_correctly() { + requuire network sudo || return 0 + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + + not testsudo librechroot -l "$roundup_test_name" run lsof + echo "CHROOTEXTRAPKG=(lsof)" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf + testsudo librechroot -l "$roundup_test_name" install-name lsof + testsudo librechroot -l "$roundup_test_name" clean-pkgs + testsudo librechroot -l "$roundup_test_name" run lsof + echo "CHROOTEXTRAPKG=()" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf + testsudo librechroot -l "$roundup_test_name" clean-pkgs + not testsudo librechroot -l "$roundup_test_name" run lsof +} + it_displays_help_as_normal_user() { rm -rf "$XDG_CONFIG_HOME" - LANG=C librechroot help >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C librechroot help >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr } it_otherwise_fails_as_normal_user() { - librechroot run true >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + librechroot -l "$roundup_test_name" run true >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } it_displays_help_and_fails_with_0_args() { - LANG=C librechroot >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + LC_ALL=C librechroot -l "$roundup_test_name" >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ "$(sed 1q $tmpdir/stderr)" =~ Usage:.* ]] + empty $tmpdir/stdout + [[ "$(sed -n 2p $tmpdir/stderr)" =~ Usage:.* ]] } # requires sudo so we know it's not failing because it needs root @@ -61,8 +102,8 @@ it_fails_for_unknown_commands() { testsudo librechroot phony >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } # requires sudo so we know it's not failing because it needs root @@ -71,6 +112,6 @@ it_fails_for_unknown_flags() { testsudo librechroot -q >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } diff --git a/test/librefetch-test.sh b/test/librefetch-test.sh new file mode 100644 index 0000000..92f67a1 --- /dev/null +++ b/test/librefetch-test.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env roundup + +describe librefetch + +. ./test-common.sh + +before() { + _before + + mkdir -p "$HOME" + printf '%s\n' \ + 'DLAGENTS+=({https,libre}'\''::/usr/bin/librefetch -p "$BUILDFILE" -- %u %o'\'')' \ + 'BUILDDIR=""' \ + > "$HOME/.makepkg.conf" + + mkdir -p "$XDG_CONFIG_HOME/libretools" + printf '%s\n' \ + 'MIRROR="phony://example.com/dir/"' \ + 'DOWNLOADER=/usr/bin/false' \ + > "$XDG_CONFIG_HOME/libretools/librefetch.conf" +} + +after() { + _after +} + +it_displays_help() { + LC_ALL=C librefetch -h >$tmpdir/stdout 2>$tmpdir/stderr + + [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] + empty $tmpdir/stderr +} + +it_cleans_src_libre_first() { + cp librefetch.d/* "$tmpdir/" + cd "$tmpdir" + + # create garbage + mkdir -p src-libre/foo + touch src-libre/foo/file + + # run librefetch + makepkg -g + + srcball=src/testpkg-1.0.tar.gz + bsdtar tf "$srcball" > list-pkg.txt + diff -u list.txt list-pkg.txt +} diff --git a/test/librefetch.d/PKGBUILD b/test/librefetch.d/PKGBUILD new file mode 100644 index 0000000..723d82e --- /dev/null +++ b/test/librefetch.d/PKGBUILD @@ -0,0 +1,15 @@ +pkgname=testpkg +pkgver=1.0 +pkgrel=1 +pkgdesc=foo +arch=(any) +source=("libre://$pkgname-$pkgver.tar.gz") + +mksource() { + mkdir "$srcdir/bar" + touch "$srcdir/bar/file" +} + +package() { + :; +} diff --git a/test/librefetch.d/list.txt b/test/librefetch.d/list.txt new file mode 100644 index 0000000..b04d5d1 --- /dev/null +++ b/test/librefetch.d/list.txt @@ -0,0 +1,2 @@ +bar/ +bar/file diff --git a/test/librelib-test.sh b/test/librelib-test.sh index e86dffe..a74ce0a 100644 --- a/test/librelib-test.sh +++ b/test/librelib-test.sh @@ -16,7 +16,7 @@ it_displays_help_and_fails_with_0_args() { ret=0 librelib >$tmpdir/stdout 2>$tmpdir/stderr || ret=$? - [[ -z "$(cat $tmpdir/stdout)" ]] + empty $tmpdir/stdout [[ "$(sed 1q $tmpdir/stderr)" =~ Usage:.* ]] [[ $ret != 0 ]] } @@ -25,8 +25,8 @@ it_fails_with_2_args() { ret=0 librelib a b >$tmpdir/stdout 2>$tmpdir/stderr || ret=$? - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr [[ $ret != 0 ]] } @@ -34,7 +34,7 @@ it_displays_usage_text() { librelib -h >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr } # libremessages is executable @@ -67,7 +67,7 @@ it_fails_to_find_phony() { ret=0 librelib phony >$tmpdir/stdout 2>$tmpdir/stderr || ret=$? - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr [[ $ret != 0 ]] } diff --git a/test/libremakepkg-test.sh b/test/libremakepkg-test.sh index be19e17..4d8a7c5 100644 --- a/test/libremakepkg-test.sh +++ b/test/libremakepkg-test.sh @@ -4,6 +4,8 @@ describe libremakepkg . ./test-common.sh +_setup_chrootdir + before() { _before libremakepkg @@ -11,7 +13,7 @@ before() { echo "BLACKLIST=https://repo.parabolagnulinux.org/docs/blacklist.txt" >"$XDG_CONFIG_HOME"/libretools/libretools.conf - echo "CHROOTDIR='$tmpdir/chrootdir'" > "$XDG_CONFIG_HOME"/libretools/chroot.conf + echo "CHROOTDIR='${chrootdir}'" > "$XDG_CONFIG_HOME"/libretools/chroot.conf echo "CHROOT='default'" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf echo "CHROOTEXTRAPKG=()" >> "$XDG_CONFIG_HOME"/libretools/chroot.conf } @@ -26,28 +28,90 @@ it_builds_a_trivial_package() { cd "$tmpdir" libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty - testsudo libremakepkg + testsudo libremakepkg -l "$roundup_test_name" [[ -f $(echo libretools-hello-1.0-1-any.pkg.tar.?z) ]] } +it_enables_networking_during_prepare() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-netprepare "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + testsudo libremakepkg -l "$roundup_test_name" + [[ -f $(echo libretools-netprepare-1.0-1-any.pkg.tar.?z) ]] +} + +it_disables_networking_during_build() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-netbuild "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + not testsudo libremakepkg -l "$roundup_test_name" + not [[ -f $(echo libretools-netbuild-1.0-1-any.pkg.tar.?z) ]] + testsudo libremakepkg -l "$roundup_test_name" -N + [[ -f $(echo libretools-netbuild-1.0-1-any.pkg.tar.?z) ]] +} + +it_disables_networking_during_package() { + require network sudo || return 0 + cp libremakepkg.d/PKGBUILD-netpackage "$tmpdir/PKGBUILD" + cd "$tmpdir" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + not testsudo libremakepkg -l "$roundup_test_name" + not [[ -f $(echo libretools-netpackage-1.0-1-any.pkg.tar.?z) ]] + testsudo libremakepkg -l "$roundup_test_name" -N + [[ -f $(echo libretools-netpackage-1.0-1-any.pkg.tar.?z) ]] +} + +it_cleans_the_chroot_before_building() { + require network sudo || return 0 + # 1. First, we build testpkg1 + # 2. Then, we build testpkg2, which depends on testpkg1 + # Therefore, testpkg1 will be installed after testpkg2 is built, we + # check for that. + # 3. Then, we build hello, which depends on neither, so testpkg1 should + # be removed. + + # Also, do funny things with the output of libremakepkg to get a helpful + # fail case. + + mkdir -p "$tmpdir"/{1,2,3} + cp libremakepkg.d/PKGBUILD-testpkg1 "$tmpdir/1/PKGBUILD" + cp libremakepkg.d/PKGBUILD-testpkg2 "$tmpdir/2/PKGBUILD" + cp libremakepkg.d/PKGBUILD-hello "$tmpdir/3/PKGBUILD" + + libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty + cd "$tmpdir/1" + testsudo libremakepkg -l "$roundup_test_name" &> "$tmpdir/out" || { r=$?; tail "$tmpdir/out"|cat -v; return $r; } + cd "$tmpdir/2" + testsudo libremakepkg -l "$roundup_test_name" &> "$tmpdir/out" || { r=$?; tail "$tmpdir/out"|cat -v; return $r; } + testsudo librechroot -l "$roundup_test_name" run libretools-testpkg1 'first time, pass' + cd "$tmpdir/3" + testsudo libremakepkg -l "$roundup_test_name" &> "$tmpdir/out" || { r=$?; tail "$tmpdir/out"|cat -v; return $r; } + not testsudo librechroot -l "$roundup_test_name" run libretools-testpkg1 'second time, fail' +} + it_handles_PKGDEST_not_existing() { require network sudo || return 0 cp libremakepkg.d/PKGBUILD-hello "$tmpdir/PKGBUILD" cd "$tmpdir" libremessages msg 'Creating a chroot, may take a few minutes' &>/dev/tty - testsudo env PKGDEST="$tmpdir/dest/pkgdest" libremakepkg + testsudo env PKGDEST="$tmpdir/dest/pkgdest" libremakepkg -l "$roundup_test_name" [[ -f $(echo dest/pkgdest/libretools-hello-1.0-1-any.pkg.tar.?z) ]] } it_displays_help_as_normal_user() { rm -rf "$XDG_CONFIG_HOME" - LANG=C libremakepkg -h >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C libremakepkg -h >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr } it_otherwise_fails_as_normal_user() { @@ -58,6 +122,6 @@ it_otherwise_fails_as_normal_user() { libremakepkg >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } diff --git a/test/libremakepkg.d/PKGBUILD-netbuild b/test/libremakepkg.d/PKGBUILD-netbuild new file mode 100644 index 0000000..7be896b --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-netbuild @@ -0,0 +1,17 @@ +pkgname='libretools-netbuild' +pkgver=1.0 +license=('GPL') +url='https://parabolagnulinux.org' + +pkgrel=1 +arch=(any) + +build() { + cd "$srcdir" + curl https://repo.parabolagnulinux.org/ > index.html +} + +package() { + cd "$srcdir" + install -Dm644 index.html "$pkgdir"/usr/share/$pkgname/index.html +} diff --git a/test/libremakepkg.d/PKGBUILD-netpackage b/test/libremakepkg.d/PKGBUILD-netpackage new file mode 100644 index 0000000..cbefe8f --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-netpackage @@ -0,0 +1,12 @@ +pkgname='libretools-netpackage' +pkgver=1.0 +license=('GPL') +url='https://parabolagnulinux.org' + +pkgrel=1 +arch=(any) + +package() { + install -d "$pkgdir"/usr/share/$pkgname + curl https://repo.parabolagnulinux.org/ > "$pkgdir"/usr/share/$pkgname/index.html +} diff --git a/test/libremakepkg.d/PKGBUILD-netprepare b/test/libremakepkg.d/PKGBUILD-netprepare new file mode 100644 index 0000000..90f3e82 --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-netprepare @@ -0,0 +1,17 @@ +pkgname='libretools-netprepare' +pkgver=1.0 +license=('GPL') +url='https://parabolagnulinux.org' + +pkgrel=1 +arch=(any) + +prepare() { + cd "$srcdir" + curl https://repo.parabolagnulinux.org/ > index.html +} + +package() { + cd "$srcdir" + install -Dm644 index.html "$pkgdir"/usr/share/$pkgname/index.html +} diff --git a/test/libremakepkg.d/PKGBUILD-testpkg1 b/test/libremakepkg.d/PKGBUILD-testpkg1 new file mode 100644 index 0000000..b7961bb --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-testpkg1 @@ -0,0 +1,19 @@ +pkgname='libretools-testpkg1' +pkgver=1.0 +license=('GPL') +url='https://parabolagnulinux.org' + +pkgrel=1 +arch=(any) +depends=(sh) + +build() { + cd "$srcdir" + echo '#!/bin/sh' > testpkg1.sh + echo 'echo testpkg1' >> testpkg1.sh +} + +package() { + cd "$srcdir" + install -Dm755 testpkg1.sh "$pkgdir"/usr/bin/libretools-testpkg1 +} diff --git a/test/libremakepkg.d/PKGBUILD-testpkg2 b/test/libremakepkg.d/PKGBUILD-testpkg2 new file mode 100644 index 0000000..11f7fe8 --- /dev/null +++ b/test/libremakepkg.d/PKGBUILD-testpkg2 @@ -0,0 +1,19 @@ +pkgname='libretools-testpkg2' +pkgver=1.0 +license=('GPL') +url='https://parabolagnulinux.org' + +pkgrel=1 +arch=(any) +depends=(sh libretools-testpkg1) + +build() { + cd "$srcdir" + echo '#!/bin/sh' > testpkg2.sh + echo 'libretools-testpkg1' >> testpkg2.sh +} + +package() { + cd "$srcdir" + install -Dm755 testpkg2.sh "$pkgdir"/usr/bin/libretools-testpkg2 +} diff --git a/test/librerelease-test.sh b/test/librerelease-test.sh new file mode 100644 index 0000000..a44b150 --- /dev/null +++ b/test/librerelease-test.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env roundup + +describe librestage + +. ./test-common.sh + +before() { + _before + + mkdir -p $XDG_CONFIG_HOME/libretools + { + echo "WORKDIR='$tmpdir/workdir'" + echo 'REPODEST=repo@repo:/srv/http/repo/staging-$LIBREUSER' + } >$XDG_CONFIG_HOME/libretools/libretools.conf + { + echo 'PKGEXT=.pkg.tar.gz' + echo "PKGDEST='$tmpdir/workdir/pkgdest'" + echo "GPGKEY=YOURKEY" + } > $HOME/.makepkg.conf + mkdir -p "$tmpdir/workdir/pkgdest" +} + +after() { + _after +} + +it_displays_usage_text() { + rm -rf "$XDG_CONFIG_HOME" + LC_ALL=C librerelease -h >"$tmpdir/stdout" 2>"$tmpdir/stderr" + + [[ "$(sed 1q "$tmpdir/stdout")" =~ Usage:.* ]] + empty "$tmpdir/stderr" +} + +it_lists_all_files() { + WORKDIR="$tmpdir/workdir" + mkdir -p "$WORKDIR/staging/repo1" "$WORKDIR/staging/repo2/sub" + touch \ + "$WORKDIR/staging/repo1/file1" \ + "$WORKDIR/staging/repo1/file2" \ + "$WORKDIR/staging/repo2/file with spaces" \ + "$WORKDIR/staging/repo2/sub/subfolder" + unset WORKDIR + LC_ALL=C librerelease -l &>"$tmpdir/list" + + cat > "$tmpdir/list-correct" <<EOF + -> repo1 + file1 + file2 + -> repo2 + file with spaces + sub/subfolder +EOF + + diff "$tmpdir/list-correct" "$tmpdir/list" +} diff --git a/test/librestage-test.sh b/test/librestage-test.sh index 849a68c..460eb58 100644 --- a/test/librestage-test.sh +++ b/test/librestage-test.sh @@ -23,26 +23,26 @@ after() { it_displays_usage_text() { rm -rf "$XDG_CONFIG_HOME" - LANG=C librestage -h >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C librestage -h >$tmpdir/stdout 2>$tmpdir/stderr [[ "$(sed 1q "$tmpdir/stdout")" =~ Usage:.* ]] - [[ -z "$(cat "$tmpdir/stderr")" ]] + empty "$tmpdir/stderr" } it_fails_with_0_args() { librestage >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat "$tmpdir/stdout")" ]] - [[ -n "$(cat "$tmpdir/stderr")" ]] + empty "$tmpdir/stdout" + not empty "$tmpdir/stderr" } it_fails_with_invalid_args() { librestage -q >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? [[ $stat != 0 ]] - [[ -z "$(cat "$tmpdir/stdout")" ]] - [[ -n "$(cat "$tmpdir/stderr")" ]] + empty "$tmpdir/stdout" + not empty "$tmpdir/stderr" } # This was an actual bug I hit with md/emacs-mdmua a long time ago; solution was diff --git a/test/pkgbuild-check-licenses-test.sh b/test/pkgbuild-check-licenses-test.sh deleted file mode 100644 index 220ef6b..0000000 --- a/test/pkgbuild-check-licenses-test.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env roundup - -# avoid carpel tunnel -pcl=pkgbuild-check-licenses - -describe $pcl - -. ./test-common.sh - -before() { - _before -} - -after() { - _after -} - -it_displays_usage_text() { - # This test seems silly, but it makes sure that it is executable, - # syntactically correct, and loading libraries works. - LANG=C $pcl -h >$tmpdir/stdout 2>$tmpdir/stderr - stat=$? - - [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] - [[ -z "$(cat $tmpdir/stderr)" ]] - [[ $stat == 0 ]] -} diff --git a/test/pkgbuild-check-nonfree-test.sh b/test/pkgbuild-check-nonfree-test.sh index d547e24..2af2669 100644 --- a/test/pkgbuild-check-nonfree-test.sh +++ b/test/pkgbuild-check-nonfree-test.sh @@ -2,6 +2,7 @@ # avoid carpel tunnel pcn=pkgbuild-check-nonfree +psn=pkgbuild-summarize-nonfree describe $pcn @@ -26,36 +27,42 @@ after() { it_displays_usage_text() { # This test seems silly, but it makes sure that it is executable, # syntactically correct, and loading libraries works. - LANG=C $pcn -h >$tmpdir/stdout 2>$tmpdir/stderr + LC_ALL=C $pcn -h >$tmpdir/stdout 2>$tmpdir/stderr stat=$? [[ "$(sed 1q $tmpdir/stdout)" =~ Usage:.* ]] - [[ -z "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stderr [[ $stat == 0 ]] } it_succeeds_for_free_depends() { $pcn $pcn.d/PKGBUILD.free >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr [[ $stat == 0 ]] } it_succeeds_for_nonfree_depend_with_replacement() { $pcn $pcn.d/PKGBUILD.nonfree-replacement >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr [[ $stat == 0 ]] } -it_fails_with_15_for_nonfree_depend() { +it_fails_for_nonfree_depend() { $pcn $pcn.d/PKGBUILD.nonfree >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] - [[ $stat == 15 ]] + local pcn_stat=$stat + + $psn $pcn_stat >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? + [[ $stat != 0 ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr } it_fails_when_there_is_no_blacklist() { @@ -65,7 +72,7 @@ it_fails_when_there_is_no_blacklist() { $pcn $pcn.d/PKGBUILD.free >$tmpdir/stdout 2>$tmpdir/stderr || stat=$? - [[ -z "$(cat $tmpdir/stdout)" ]] - [[ -n "$(cat $tmpdir/stderr)" ]] + empty $tmpdir/stdout + not empty $tmpdir/stderr [[ $stat != 0 ]] && [[ $stat != 15 ]] } diff --git a/test/test-common.sh b/test/test-common.sh index 01b1b35..c525dd8 100644 --- a/test/test-common.sh +++ b/test/test-common.sh @@ -6,7 +6,12 @@ if [[ $HOME == "$(eval echo ~$USER)" ]]; then fi _before() { + unset PKGDEST SRCDEST SRCPKGDEST LOGDEST + unset BUILDDIR + unset PKGEXT SRCEXT + unset GPGKEY PACKAGER tmpdir="$(mktemp -d --tmpdir "test-${roundup_desc//\//-}.XXXXXXXXXXXX")" + chmod 755 "$tmpdir" stat=0 } @@ -22,7 +27,31 @@ _after_sudo() { fi } -require() { +_setup_chrootdir() { + if [[ -z "$chrootdir" ]]; then + export chrootdir="$(mktemp -d --tmpdir "test-chrootdir.XXXXXXXXXXXX")" + trap "$(printf '_cleanup_chrootdir %q' "$chrootdir")" EXIT + fi +} + +_cleanup_chrootdir() ( + chrootdir=$1 + shopt -s nullglob + if [[ $SUDO ]]; then + for copydir in "$chrootdir"/*/*/; do + local chroottype=$(stat -f -c %T "$copydir") + if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then + sudo btrfs subvolume delete "$copydir" >/dev/null + fi + done + sudo rm -rf -- "$chrootdir" + else + rm -rf -- "$chrootdir" + fi +) + +require() ( + set +x local missing=() if libremessages in_array "network" "$@" && ! [[ $NETWORK ]]; then missing+=('networking') @@ -35,4 +64,15 @@ require() { return 1 fi return 0; -} +) + +empty() ( + set +x + [[ $(stat -c %s "$1") -eq 0 ]] +) + +# Just using '!' doesn't trip `set -e` +not() ( + set +x + ! eval "$@" +) diff --git a/test/testenv b/test/testenv index e6c882f..847a948 100755 --- a/test/testenv +++ b/test/testenv @@ -15,6 +15,11 @@ while [[ $# -gt 0 ]]; do done export NETWORK SUDO +if [[ $# == 0 ]]; then + echo 'You need to run testenv with arguments!' >&2 + exit 1 +fi + # Set up the working directory, and add the hook to clean it up export TMPDIR="$(mktemp --tmpdir -d libretools-test.XXXXXXXXXX)" trap "rm -rf '$TMPDIR'" EXIT |