From 6ea37b3751e7e5687eaf50e0ac957c18a66e3b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20PORTAY?= Date: Thu, 9 Nov 2023 14:59:18 +0100 Subject: [PATCH] cqfd: add exec command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TL;DR; The command run takes arguments as if they are given to system() (i.e. the command_string is intepreted by sh -c). The command shell takes arguments as if they are given to execlp($SHELL, ...), and the new command exec takes arguments as they are given to execlp() (i.e. for the two last, the command is not interpreted by sh; except for the arguments that are not simple-quoted and that are subject to get interpreted by the current shell). In short: - cqfd run -> sh -c "$*" - cqfd shell -> sh "$@" - cqfd exec -> ssh "$@" This adds the exec command to run a command from within the container. The command consists of the exec-program and its arguments (if any), as it is given to the C function execlp(). The exec-program could be either a binary or a script. $ cqfd exec COMMAND [ARGUMENTS...] It is a distinct CLI to the commands run and shell that both takes a command_string as it is given to the C function system() (i.e. given to sh -c): - cqfd shell has three different forms: cqfd shell [command_file [argument...]] cqfd shell -c command_string [command_name [argument...]] cqfd shell -s [argument...] (requires CQFD_EXTRA_RUN_ARGS=-i for now) - cqfd run [-c] [command_string] takes a command_string as specified by sh(1) or the command= attribute from the file .cqfdrc. Important: the option -c of run is for concatenate and it is different to the option -c of sh(1). Note: The command exec (as the command shell) does not run the command= set in the file .cqfdrc. According to sh(1): argument The positional parameters ($1, $2, and so on) shall be set to arguments, if any. command_file The pathname of a file containing commands. If the pathname contains one or more characters, the implementation attempts to read that file; the file need not be executable. If the pathname does not contain a character: * The implementation shall attempt to read that file from the current working directory; the file need not be executable. * If the file is not in the current working directory, the implementation may perform a search for an executable file using the value of PATH, as described in Section 2.9.1.1, Command Search and Execution. Special parameter 0 (see Section 2.5.2, Special Parameters) shall be set to the value of command_file. If sh is called using a synopsis form that omits command_file, special parameter 0 shall be set to the value of the first argument passed to sh from its parent (for example, argv[0] for a C program), which is normally a pathname used to execute the sh utility. command_name A string assigned to special parameter 0 when executing the commands in command_string. If command_name is not specified, special parameter 0 shall be set to the value of the first argument passed to sh from its parent (for example, argv[0] for a C program), which is normally a pathname used to execute the sh utility. command_string A string that shall be interpreted by the shell as one or more commands, as if the string were the argument to the system() function defined in the System Interfaces volume of POSIX.1‐2017. If the command_string operand is an empty string, sh shall exit with a zero exit status. Fixes: #99 --- README.md | 7 +++++-- bash-completion | 12 ++++++++---- cqfd | 11 +++++++++++ tests/07-cqfd_exec | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100755 tests/07-cqfd_exec diff --git a/README.md b/README.md index 8173341..f2c10ed 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" Additionally, you may want to open an interactive shell: diff --git a/bash-completion b/bash-completion index cdd6fa8..d76f5ab 100644 --- a/bash-completion +++ b/bash-completion @@ -48,9 +48,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 @@ -61,7 +61,11 @@ _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 for (( i=1; i <= cword; i++ )); do @@ -84,7 +88,7 @@ _cqfd() { return fi - local cmds="init flavors run release shell version help" + local cmds="init flavors exec run release shell version help" COMPREPLY=( $(compgen -W "$cmds $opts" -- "$cur") ) } && complete -F _cqfd cqfd diff --git a/cqfd b/cqfd index a43d662..bb7bad3 100755 --- a/cqfd +++ b/cqfd @@ -44,6 +44,7 @@ Options: Commands: init Initialize project build container. flavors List flavors from config file to stdout. + exec cmd [args] Run argument(s) inside build container. run [cmdstring] Run argument(s) inside build container. release [cmdstring] Run argument(s) and release software. shell [shargs] Run shell command inside build container. @@ -493,6 +494,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 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