Skip to content

Commit

Permalink
cqfd: add exec command
Browse files Browse the repository at this point in the history
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 <slash> characters, the implementation
	attempts to read that file; the file need not be executable. If
	the pathname does not contain a <slash> 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: savoirfairelinux#99
  • Loading branch information
gportay committed Jan 14, 2024
1 parent 916c84f commit ed64fce
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 6 deletions.
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 8 additions & 4 deletions bash-completion
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
11 changes: 11 additions & 0 deletions cqfd
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -519,6 +520,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
Expand Down
40 changes: 40 additions & 0 deletions tests/07-cqfd_exec
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit ed64fce

Please sign in to comment.