diff --git a/README.md b/README.md index 51a9a99..181f47e 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,13 @@ default build command as configured in .cqfdrc, use: $ cqfd -Alternatively, you may want to specify a custom build command to be +Alternatively, you may want to specify a single custom command to be executed from inside the build container. - $ cqfd run make clean + $ cqfd exec make clean + +Or custom commands composed with shell grammar: + $ cqfd run "make linux-dirclean && make foobar-dirclean" When ``cqfd`` is running, the current directory is mounted by Docker diff --git a/bash-completion b/bash-completion index 6d64549..da7b429 100644 --- a/bash-completion +++ b/bash-completion @@ -55,9 +55,9 @@ _cqfd() { local arg= _get_first_arg - if [[ "$arg" == run ]]; then + if [[ "$arg" =~ ^(exec|run)$ ]]; then for (( i=1; i <= cword; i++ )); do - if [[ ${words[i]} == run ]]; then + if [[ ${words[i]} =~ ^(exec|run)$ ]]; then if [[ $((i+1)) -eq $cword ]]; then break elif [[ ${words[i+1]} == -c ]]; then @@ -68,11 +68,15 @@ _cqfd() { fi done - COMPREPLY=( $(compgen -c -W "-c" -- "$cur") ) + if [[ "$arg" == exec ]]; then + COMPREPLY=( $(compgen -c -- "$cur") ) + else + COMPREPLY=( $(compgen -c -W "-c" -- "$cur") ) + fi return - elif [[ "$arg" == shell ]]; then + elif [[ "$arg" =~ ^(shell|sh|ash|dash|bash|ksh|zsh|csh|tcsh|fish)$ ]]; then for (( i=1; i <= cword; i++ )); do - if [[ ${words[i]} == shell ]]; then + if [[ ${words[i]} == "$arg" ]]; then ((i++)) break fi @@ -91,7 +95,8 @@ _cqfd() { return fi - local cmds="init flavors run release shell version help" - COMPREPLY=( $(compgen -W "$cmds $opts" -- "$cur") ) + local shells="sh ash dash bash ksh zsh csh tcsh fish" + local cmds="init flavors exec run release shell version help" + COMPREPLY=( $(compgen -W "$shells $cmds $opts" -- "$cur") ) } && complete -F _cqfd cqfd diff --git a/cqfd b/cqfd index 0b85dbb..9c596bd 100755 --- a/cqfd +++ b/cqfd @@ -45,6 +45,7 @@ Options: Commands: init Initialize project build container. + exec cmd [args] Run argument(s) inside build container. flavors List flavors from config file to stdout. run [cmdstring] Run argument(s) inside build container. release [cmdstring] Run argument(s) and release software. @@ -388,8 +389,12 @@ test -n "\$failed" && test_su_session_command && has_su_session_command=1 # Add the host's user and group to the container, and adjust ownership. +if ! shell=\$(command -v $cqfd_shell); then + echo "$cqfd_shell: command not found" >&2 + exit 127 +fi groupadd -og $GROUPS -f builders || die "groupadd command failed." -useradd -s $cqfd_shell -oN -u $UID -g $GROUPS -d "$cqfd_user_home" $cqfd_user \ +useradd -s \$shell -oN -u $UID -g $GROUPS -d "$cqfd_user_home" $cqfd_user \ || die "useradd command failed." mkdir -p "$cqfd_user_home" || die "mkdir command failed." chown $UID:$GROUPS "$cqfd_user_home" || die "chown command failed." @@ -565,6 +570,16 @@ while [ $# -gt 0 ]; do -q) quiet=true ;; + exec) + if [ "$#" -lt 2 ]; then + die "command exec requires arguments" + fi + shift + config_load $flavor + command_string="${@@Q}" + docker_run "$command_string" + exit + ;; run|release) if [ "$1" = "release" ]; then has_to_release=true @@ -589,7 +604,10 @@ while [ $# -gt 0 ]; do has_alternate_command=true break ;; - shell) + sh|ash|dash|bash|ksh|zsh|csh|tcsh|fish|shell) + if [ "$1" != "shell" ]; then + cqfd_shell="$1" + fi shift config_load "$flavor" command_string="$cqfd_shell" diff --git a/tests/07-cqfd_exec b/tests/07-cqfd_exec new file mode 100755 index 0000000..030ad53 --- /dev/null +++ b/tests/07-cqfd_exec @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# +# validate the behavior of exec command + +set -o pipefail + +. "$(dirname "$0")"/jtest.inc "$1" +cqfd="$TDIR/.cqfd/cqfd" + +cd $TDIR/ + +jtest_prepare "cqfd exec with no argument should fail" +if ! $cqfd exec; then + jtest_result pass +else + jtest_result fail +fi + +jtest_prepare "cqfd exec with argument should succeed" +if $cqfd exec true; then + jtest_result pass +else + jtest_result fail +fi + +jtest_prepare "cqfd exec should return same status" +if $cqfd exec exit 10; + test "$?" -eq 10; then + jtest_result pass +else + jtest_result fail +fi + +jtest_prepare "cqfd exec should preserve the arguments" +if $cqfd exec /bin/sh -c 'printf "0=$0,*=$*,#=$#"' zero one two three \ + | grep "0=zero,\*=one two three,#=3$"; then + jtest_result pass +else + jtest_result fail +fi diff --git a/tests/08-cqfd_shell b/tests/08-cqfd_shell index 2ae0758..870d13e 100755 --- a/tests/08-cqfd_shell +++ b/tests/08-cqfd_shell @@ -33,9 +33,43 @@ else jtest_result fail fi -jtest_prepare "cqfd shell should fail with 127 if \$CQFD_SHELL is not found" -if CQFD_SHELL=/bin/non-existant-shell $cqfd shell <<<"tty"; - test "$?" -eq 127; then +jtest_prepare "cqfd shell should fail with 127 and '/bin/non-existant-shell: command not found'" +if CQFD_SHELL=/bin/non-existant-shell $cqfd shell <<<"tty" \ + | tee /dev/stderr | grep -q '/bin/non-existant-shell: command not found'; + test "${PIPESTATUS[0]}" -eq 127; then + jtest_result pass +else + jtest_result fail +fi + +jtest_prepare "cqfd sh should succeed and use sh" +if $cqfd sh <<<'printf "$0"' \ + | tee /dev/stderr | grep -q "^sh$"; then + jtest_result pass +else + jtest_result fail +fi + +jtest_prepare "cqfd bash should succeed and use bash" +if $cqfd bash <<<'printf "$0"' \ + | tee /dev/stderr | grep -q "^bash$"; then + jtest_result pass +else + jtest_result fail +fi + +jtest_prepare "cqfd dash should succeed and use dash" +if $cqfd dash <<<'printf "$0"' \ + | tee /dev/stderr | grep -q "^dash$"; then + jtest_result pass +else + jtest_result fail +fi + +jtest_prepare "cqfd zsh should fail with 127 and 'zsh: command not found'" +if $cqfd zsh 2>&1 <<<'printf "$0"' \ + | tee /dev/stderr | grep -q 'zsh: command not found'; + test "${PIPESTATUS[0]}" -eq 127; then jtest_result pass else jtest_result fail